diff --git a/crates/ruff_db/src/diagnostic/mod.rs b/crates/ruff_db/src/diagnostic/mod.rs index b795911d6d..b3b82486b3 100644 --- a/crates/ruff_db/src/diagnostic/mod.rs +++ b/crates/ruff_db/src/diagnostic/mod.rs @@ -232,6 +232,15 @@ impl Diagnostic { pub fn primary_tags(&self) -> Option<&[DiagnosticTag]> { self.primary_annotation().map(|ann| ann.tags.as_slice()) } + + /// Returns a key that can be used to sort two diagnostics into the canonical order + /// in which they should appear when rendered. + pub fn rendering_sort_key<'a>(&'a self, db: &'a dyn Db) -> impl Ord + 'a { + RenderingSortKey { + db, + diagnostic: self, + } + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -243,6 +252,64 @@ struct DiagnosticInner { subs: Vec, } +struct RenderingSortKey<'a> { + db: &'a dyn Db, + diagnostic: &'a Diagnostic, +} + +impl Ord for RenderingSortKey<'_> { + // We sort diagnostics in a way that keeps them in source order + // and grouped by file. After that, we fall back to severity + // (with fatal messages sorting before info messages) and then + // finally the diagnostic ID. + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + if let (Some(span1), Some(span2)) = ( + self.diagnostic.primary_span(), + other.diagnostic.primary_span(), + ) { + let order = span1 + .file() + .path(self.db) + .as_str() + .cmp(span2.file().path(self.db).as_str()); + if order.is_ne() { + return order; + } + + if let (Some(range1), Some(range2)) = (span1.range(), span2.range()) { + let order = range1.start().cmp(&range2.start()); + if order.is_ne() { + return order; + } + } + } + // Reverse so that, e.g., Fatal sorts before Info. + let order = self + .diagnostic + .severity() + .cmp(&other.diagnostic.severity()) + .reverse(); + if order.is_ne() { + return order; + } + self.diagnostic.id().cmp(&other.diagnostic.id()) + } +} + +impl PartialOrd for RenderingSortKey<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for RenderingSortKey<'_> { + fn eq(&self, other: &Self) -> bool { + self.cmp(other).is_eq() + } +} + +impl Eq for RenderingSortKey<'_> {} + /// A collection of information subservient to a diagnostic. /// /// A sub-diagnostic is always rendered after the parent diagnostic it is diff --git a/crates/ty_project/src/lib.rs b/crates/ty_project/src/lib.rs index 3a91a00362..54eb3ebbcf 100644 --- a/crates/ty_project/src/lib.rs +++ b/crates/ty_project/src/lib.rs @@ -219,34 +219,10 @@ impl Project { .unwrap() .into_inner() .unwrap(); - // We sort diagnostics in a way that keeps them in source order - // and grouped by file. After that, we fall back to severity - // (with fatal messages sorting before info messages) and then - // finally the diagnostic ID. - file_diagnostics.sort_by(|d1, d2| { - if let (Some(span1), Some(span2)) = (d1.primary_span(), d2.primary_span()) { - let order = span1 - .file() - .path(db) - .as_str() - .cmp(span2.file().path(db).as_str()); - if order.is_ne() { - return order; - } - if let (Some(range1), Some(range2)) = (span1.range(), span2.range()) { - let order = range1.start().cmp(&range2.start()); - if order.is_ne() { - return order; - } - } - } - // Reverse so that, e.g., Fatal sorts before Info. - let order = d1.severity().cmp(&d2.severity()).reverse(); - if order.is_ne() { - return order; - } - d1.id().cmp(&d2.id()) + file_diagnostics.sort_by(|left, right| { + left.rendering_sort_key(db) + .cmp(&right.rendering_sort_key(db)) }); diagnostics.extend(file_diagnostics); diagnostics diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/for.md_-_For_loops_-_With_non-callable_iterator.snap b/crates/ty_python_semantic/resources/mdtest/snapshots/for.md_-_For_loops_-_With_non-callable_iterator.snap index e8413493be..dad3e73977 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/for.md_-_For_loops_-_With_non-callable_iterator.snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/for.md_-_For_loops_-_With_non-callable_iterator.snap @@ -46,6 +46,18 @@ info: `lint:not-iterable` is enabled by default ``` +``` +info: revealed-type: Revealed type + --> src/mdtest_snippet.py:16:5 + | +14 | # revealed: Unknown +15 | # error: [possibly-unresolved-reference] +16 | reveal_type(x) + | ^^^^^^^^^^^^^^ `Unknown` + | + +``` + ``` warning: lint:possibly-unresolved-reference: Name `x` used when possibly not defined --> src/mdtest_snippet.py:16:17 @@ -58,15 +70,3 @@ warning: lint:possibly-unresolved-reference: Name `x` used when possibly not def info: `lint:possibly-unresolved-reference` is enabled by default ``` - -``` -info: revealed-type: Revealed type - --> src/mdtest_snippet.py:16:5 - | -14 | # revealed: Unknown -15 | # error: [possibly-unresolved-reference] -16 | reveal_type(x) - | ^^^^^^^^^^^^^^ `Unknown` - | - -``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___Legacy_syntax_-_Inferring_a_bound_typevar.snap b/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___Legacy_syntax_-_Inferring_a_bound_typevar.snap index 28dde2512e..d852c3ee9b 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___Legacy_syntax_-_Inferring_a_bound_typevar.snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___Legacy_syntax_-_Inferring_a_bound_typevar.snap @@ -55,6 +55,18 @@ info: revealed-type: Revealed type ``` +``` +info: revealed-type: Revealed type + --> src/mdtest_snippet.py:12:1 + | +10 | reveal_type(f(True)) # revealed: Literal[True] +11 | # error: [invalid-argument-type] +12 | reveal_type(f("string")) # revealed: Unknown + | ^^^^^^^^^^^^^^^^^^^^^^^^ `Unknown` + | + +``` + ``` error: lint:invalid-argument-type: Argument to this function is incorrect --> src/mdtest_snippet.py:12:15 @@ -77,15 +89,3 @@ info: Type variable defined here info: `lint:invalid-argument-type` is enabled by default ``` - -``` -info: revealed-type: Revealed type - --> src/mdtest_snippet.py:12:1 - | -10 | reveal_type(f(True)) # revealed: Literal[True] -11 | # error: [invalid-argument-type] -12 | reveal_type(f("string")) # revealed: Unknown - | ^^^^^^^^^^^^^^^^^^^^^^^^ `Unknown` - | - -``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___Legacy_syntax_-_Inferring_a_constrained_typevar.snap b/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___Legacy_syntax_-_Inferring_a_constrained_typevar.snap index f080d326d8..2fd0e658c6 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___Legacy_syntax_-_Inferring_a_constrained_typevar.snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___Legacy_syntax_-_Inferring_a_constrained_typevar.snap @@ -70,6 +70,18 @@ info: revealed-type: Revealed type ``` +``` +info: revealed-type: Revealed type + --> src/mdtest_snippet.py:13:1 + | +11 | reveal_type(f(None)) # revealed: None +12 | # error: [invalid-argument-type] +13 | reveal_type(f("string")) # revealed: Unknown + | ^^^^^^^^^^^^^^^^^^^^^^^^ `Unknown` + | + +``` + ``` error: lint:invalid-argument-type: Argument to this function is incorrect --> src/mdtest_snippet.py:13:15 @@ -92,15 +104,3 @@ info: Type variable defined here info: `lint:invalid-argument-type` is enabled by default ``` - -``` -info: revealed-type: Revealed type - --> src/mdtest_snippet.py:13:1 - | -11 | reveal_type(f(None)) # revealed: None -12 | # error: [invalid-argument-type] -13 | reveal_type(f("string")) # revealed: Unknown - | ^^^^^^^^^^^^^^^^^^^^^^^^ `Unknown` - | - -``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___PEP_695_syntax_-_Inferring_a_bound_typevar.snap b/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___PEP_695_syntax_-_Inferring_a_bound_typevar.snap index 6a9c1c178d..f38b1ea321 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___PEP_695_syntax_-_Inferring_a_bound_typevar.snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___PEP_695_syntax_-_Inferring_a_bound_typevar.snap @@ -52,6 +52,18 @@ info: revealed-type: Revealed type ``` +``` +info: revealed-type: Revealed type + --> src/mdtest_snippet.py:9:1 + | +7 | reveal_type(f(True)) # revealed: Literal[True] +8 | # error: [invalid-argument-type] +9 | reveal_type(f("string")) # revealed: Unknown + | ^^^^^^^^^^^^^^^^^^^^^^^^ `Unknown` + | + +``` + ``` error: lint:invalid-argument-type: Argument to this function is incorrect --> src/mdtest_snippet.py:9:15 @@ -73,15 +85,3 @@ info: Type variable defined here info: `lint:invalid-argument-type` is enabled by default ``` - -``` -info: revealed-type: Revealed type - --> src/mdtest_snippet.py:9:1 - | -7 | reveal_type(f(True)) # revealed: Literal[True] -8 | # error: [invalid-argument-type] -9 | reveal_type(f("string")) # revealed: Unknown - | ^^^^^^^^^^^^^^^^^^^^^^^^ `Unknown` - | - -``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___PEP_695_syntax_-_Inferring_a_constrained_typevar.snap b/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___PEP_695_syntax_-_Inferring_a_constrained_typevar.snap index ae3a2e2492..c504640767 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___PEP_695_syntax_-_Inferring_a_constrained_typevar.snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/functions.md_-_Generic_functions___PEP_695_syntax_-_Inferring_a_constrained_typevar.snap @@ -67,6 +67,18 @@ info: revealed-type: Revealed type ``` +``` +info: revealed-type: Revealed type + --> src/mdtest_snippet.py:10:1 + | + 8 | reveal_type(f(None)) # revealed: None + 9 | # error: [invalid-argument-type] +10 | reveal_type(f("string")) # revealed: Unknown + | ^^^^^^^^^^^^^^^^^^^^^^^^ `Unknown` + | + +``` + ``` error: lint:invalid-argument-type: Argument to this function is incorrect --> src/mdtest_snippet.py:10:15 @@ -88,15 +100,3 @@ info: Type variable defined here info: `lint:invalid-argument-type` is enabled by default ``` - -``` -info: revealed-type: Revealed type - --> src/mdtest_snippet.py:10:1 - | - 8 | reveal_type(f(None)) # revealed: None - 9 | # error: [invalid-argument-type] -10 | reveal_type(f("string")) # revealed: Unknown - | ^^^^^^^^^^^^^^^^^^^^^^^^ `Unknown` - | - -``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Order_tests_-_`__bases__`_lists_with_duplicate_bases.snap b/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Order_tests_-_`__bases__`_lists_with_duplicate_bases.snap index f4a86f9bd3..efe5a34412 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Order_tests_-_`__bases__`_lists_with_duplicate_bases.snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Order_tests_-_`__bases__`_lists_with_duplicate_bases.snap @@ -99,6 +99,33 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/mro.md # Diagnostics +``` +error: lint:duplicate-base: Duplicate base class `str` + --> src/mdtest_snippet.py:3:7 + | +1 | from typing_extensions import reveal_type +2 | +3 | class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`" + | ^^^^^^^^^^^^^ +4 | +5 | reveal_type(Foo.__mro__) # revealed: tuple[, Unknown, ] + | +info: The definition of class `Foo` will raise `TypeError` at runtime + --> src/mdtest_snippet.py:3:11 + | +1 | from typing_extensions import reveal_type +2 | +3 | class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`" + | --- ^^^ Class `str` later repeated here + | | + | Class `str` first included in bases list here +4 | +5 | reveal_type(Foo.__mro__) # revealed: tuple[, Unknown, ] + | +info: `lint:duplicate-base` is enabled by default + +``` + ``` info: revealed-type: Revealed type --> src/mdtest_snippet.py:5:1 @@ -113,34 +140,6 @@ info: revealed-type: Revealed type ``` -``` -info: revealed-type: Revealed type - --> src/mdtest_snippet.py:27:1 - | -25 | # fmt: on -26 | -27 | reveal_type(Ham.__mro__) # revealed: tuple[, Unknown, ] - | ^^^^^^^^^^^^^^^^^^^^^^^^ `tuple[, Unknown, ]` -28 | -29 | class Mushrooms: ... - | - -``` - -``` -info: revealed-type: Revealed type - --> src/mdtest_snippet.py:32:1 - | -30 | class Omelette(Spam, Eggs, Mushrooms, Mushrooms): ... # error: [duplicate-base] -31 | -32 | reveal_type(Omelette.__mro__) # revealed: tuple[, Unknown, ] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `tuple[, Unknown, ]` -33 | -34 | # fmt: off - | - -``` - ``` error: lint:duplicate-base: Duplicate base class `Spam` --> src/mdtest_snippet.py:16:7 @@ -217,63 +216,16 @@ info: `lint:duplicate-base` is enabled by default ``` ``` -error: lint:duplicate-base: Duplicate base class `A` - --> src/mdtest_snippet.py:76:7 +info: revealed-type: Revealed type + --> src/mdtest_snippet.py:27:1 | -75 | # error: [duplicate-base] -76 | class E( - | _______^ -77 | | A, -78 | | A -79 | | ): - | |_^ -80 | # error: [unused-ignore-comment] -81 | x: int # type: ignore[duplicate-base] +25 | # fmt: on +26 | +27 | reveal_type(Ham.__mro__) # revealed: tuple[, Unknown, ] + | ^^^^^^^^^^^^^^^^^^^^^^^^ `tuple[, Unknown, ]` +28 | +29 | class Mushrooms: ... | -info: The definition of class `E` will raise `TypeError` at runtime - --> src/mdtest_snippet.py:77:5 - | -75 | # error: [duplicate-base] -76 | class E( -77 | A, - | - Class `A` first included in bases list here -78 | A - | ^ Class `A` later repeated here -79 | ): -80 | # error: [unused-ignore-comment] - | -info: `lint:duplicate-base` is enabled by default - -``` - -``` -error: lint:duplicate-base: Duplicate base class `A` - --> src/mdtest_snippet.py:69:7 - | -68 | # error: [duplicate-base] -69 | class D( - | _______^ -70 | | A, -71 | | # error: [unused-ignore-comment] -72 | | A, # type: ignore[duplicate-base] -73 | | ): ... - | |_^ -74 | -75 | # error: [duplicate-base] - | -info: The definition of class `D` will raise `TypeError` at runtime - --> src/mdtest_snippet.py:70:5 - | -68 | # error: [duplicate-base] -69 | class D( -70 | A, - | - Class `A` first included in bases list here -71 | # error: [unused-ignore-comment] -72 | A, # type: ignore[duplicate-base] - | ^ Class `A` later repeated here -73 | ): ... - | -info: `lint:duplicate-base` is enabled by default ``` @@ -302,6 +254,20 @@ info: `lint:duplicate-base` is enabled by default ``` +``` +info: revealed-type: Revealed type + --> src/mdtest_snippet.py:32:1 + | +30 | class Omelette(Spam, Eggs, Mushrooms, Mushrooms): ... # error: [duplicate-base] +31 | +32 | reveal_type(Omelette.__mro__) # revealed: tuple[, Unknown, ] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `tuple[, Unknown, ]` +33 | +34 | # fmt: off + | + +``` + ``` error: lint:duplicate-base: Duplicate base class `Eggs` --> src/mdtest_snippet.py:37:7 @@ -348,28 +314,32 @@ info: `lint:duplicate-base` is enabled by default ``` ``` -error: lint:duplicate-base: Duplicate base class `str` - --> src/mdtest_snippet.py:3:7 - | -1 | from typing_extensions import reveal_type -2 | -3 | class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`" - | ^^^^^^^^^^^^^ -4 | -5 | reveal_type(Foo.__mro__) # revealed: tuple[, Unknown, ] - | -info: The definition of class `Foo` will raise `TypeError` at runtime - --> src/mdtest_snippet.py:3:11 - | -1 | from typing_extensions import reveal_type -2 | -3 | class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`" - | --- ^^^ Class `str` later repeated here - | | - | Class `str` first included in bases list here -4 | -5 | reveal_type(Foo.__mro__) # revealed: tuple[, Unknown, ] - | +error: lint:duplicate-base: Duplicate base class `A` + --> src/mdtest_snippet.py:69:7 + | +68 | # error: [duplicate-base] +69 | class D( + | _______^ +70 | | A, +71 | | # error: [unused-ignore-comment] +72 | | A, # type: ignore[duplicate-base] +73 | | ): ... + | |_^ +74 | +75 | # error: [duplicate-base] + | +info: The definition of class `D` will raise `TypeError` at runtime + --> src/mdtest_snippet.py:70:5 + | +68 | # error: [duplicate-base] +69 | class D( +70 | A, + | - Class `A` first included in bases list here +71 | # error: [unused-ignore-comment] +72 | A, # type: ignore[duplicate-base] + | ^ Class `A` later repeated here +73 | ): ... + | info: `lint:duplicate-base` is enabled by default ``` @@ -387,6 +357,36 @@ warning: lint:unused-ignore-comment ``` +``` +error: lint:duplicate-base: Duplicate base class `A` + --> src/mdtest_snippet.py:76:7 + | +75 | # error: [duplicate-base] +76 | class E( + | _______^ +77 | | A, +78 | | A +79 | | ): + | |_^ +80 | # error: [unused-ignore-comment] +81 | x: int # type: ignore[duplicate-base] + | +info: The definition of class `E` will raise `TypeError` at runtime + --> src/mdtest_snippet.py:77:5 + | +75 | # error: [duplicate-base] +76 | class E( +77 | A, + | - Class `A` first included in bases list here +78 | A + | ^ Class `A` later repeated here +79 | ): +80 | # error: [unused-ignore-comment] + | +info: `lint:duplicate-base` is enabled by default + +``` + ``` warning: lint:unused-ignore-comment --> src/mdtest_snippet.py:81:13 diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorators_-_`@classmethod`.snap b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorators_-_`@classmethod`.snap index 88596ab9e3..645793de8a 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorators_-_`@classmethod`.snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorators_-_`@classmethod`.snap @@ -71,23 +71,6 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/overloads.md # Diagnostics -``` -error: lint:invalid-overload: Overloaded function `try_from3` does not use the `@classmethod` decorator consistently - --> src/mdtest_snippet.py:40:9 - | -38 | def try_from3(cls, x: str) -> None: ... -39 | # error: [invalid-overload] -40 | def try_from3(cls, x: int | str) -> CheckClassMethod | None: - | --------- - | | - | Missing here -41 | if isinstance(x, int): -42 | return cls(x) - | -info: `lint:invalid-overload` is enabled by default - -``` - ``` error: lint:invalid-overload: Overloaded function `try_from1` does not use the `@classmethod` decorator consistently --> src/mdtest_snippet.py:13:9 @@ -129,3 +112,20 @@ error: lint:invalid-overload: Overloaded function `try_from2` does not use the ` info: `lint:invalid-overload` is enabled by default ``` + +``` +error: lint:invalid-overload: Overloaded function `try_from3` does not use the `@classmethod` decorator consistently + --> src/mdtest_snippet.py:40:9 + | +38 | def try_from3(cls, x: str) -> None: ... +39 | # error: [invalid-overload] +40 | def try_from3(cls, x: int | str) -> CheckClassMethod | None: + | --------- + | | + | Missing here +41 | if isinstance(x, int): +42 | return cls(x) + | +info: `lint:invalid-overload` is enabled by default + +``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorators_-_`@final`.snap b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorators_-_`@final`.snap index 1062aa8bb4..19a6d9d4b7 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorators_-_`@final`.snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorators_-_`@final`.snap @@ -64,22 +64,6 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/overloads.md # Diagnostics -``` -error: lint:invalid-overload: `@final` decorator should be applied only to the overload implementation - --> src/mdtest_snippet.py:27:9 - | -25 | def method3(self, x: str) -> str: ... -26 | # error: [invalid-overload] -27 | def method3(self, x: int | str) -> int | str: - | ------- - | | - | Implementation defined here -28 | return x - | -info: `lint:invalid-overload` is enabled by default - -``` - ``` error: lint:invalid-overload: `@final` decorator should be applied only to the overload implementation --> src/mdtest_snippet.py:18:9 @@ -96,6 +80,22 @@ info: `lint:invalid-overload` is enabled by default ``` +``` +error: lint:invalid-overload: `@final` decorator should be applied only to the overload implementation + --> src/mdtest_snippet.py:27:9 + | +25 | def method3(self, x: str) -> str: ... +26 | # error: [invalid-overload] +27 | def method3(self, x: int | str) -> int | str: + | ------- + | | + | Implementation defined here +28 | return x + | +info: `lint:invalid-overload` is enabled by default + +``` + ``` error: lint:invalid-overload: `@final` decorator should be applied only to the first overload --> src/mdtest_snippet.pyi:11:9 diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/protocols.md_-_Protocols_-_Calls_to_protocol_classes.snap b/crates/ty_python_semantic/resources/mdtest/snapshots/protocols.md_-_Protocols_-_Calls_to_protocol_classes.snap index 85a453b06b..af64d08d51 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/protocols.md_-_Protocols_-_Calls_to_protocol_classes.snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/protocols.md_-_Protocols_-_Calls_to_protocol_classes.snap @@ -41,6 +41,19 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/protocols.md # Diagnostics +``` +info: revealed-type: Revealed type + --> src/mdtest_snippet.py:4:1 + | +3 | # error: [call-non-callable] +4 | reveal_type(Protocol()) # revealed: Unknown + | ^^^^^^^^^^^^^^^^^^^^^^^ `Unknown` +5 | +6 | class MyProtocol(Protocol): + | + +``` + ``` error: lint:call-non-callable: Object of type `typing.Protocol` is not callable --> src/mdtest_snippet.py:4:13 @@ -57,14 +70,14 @@ info: `lint:call-non-callable` is enabled by default ``` info: revealed-type: Revealed type - --> src/mdtest_snippet.py:4:1 - | -3 | # error: [call-non-callable] -4 | reveal_type(Protocol()) # revealed: Unknown - | ^^^^^^^^^^^^^^^^^^^^^^^ `Unknown` -5 | -6 | class MyProtocol(Protocol): - | + --> src/mdtest_snippet.py:10:1 + | + 9 | # error: [call-non-callable] "Cannot instantiate class `MyProtocol`" +10 | reveal_type(MyProtocol()) # revealed: MyProtocol + | ^^^^^^^^^^^^^^^^^^^^^^^^^ `MyProtocol` +11 | +12 | class GenericProtocol[T](Protocol): + | ``` @@ -93,13 +106,12 @@ info: `lint:call-non-callable` is enabled by default ``` info: revealed-type: Revealed type - --> src/mdtest_snippet.py:10:1 + --> src/mdtest_snippet.py:16:1 | - 9 | # error: [call-non-callable] "Cannot instantiate class `MyProtocol`" -10 | reveal_type(MyProtocol()) # revealed: MyProtocol - | ^^^^^^^^^^^^^^^^^^^^^^^^^ `MyProtocol` -11 | -12 | class GenericProtocol[T](Protocol): +15 | # error: [call-non-callable] "Cannot instantiate class `GenericProtocol`" +16 | reveal_type(GenericProtocol[int]()) # revealed: GenericProtocol[int] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `GenericProtocol[int]` +17 | class SubclassOfMyProtocol(MyProtocol): ... | ``` @@ -126,18 +138,6 @@ info: `lint:call-non-callable` is enabled by default ``` -``` -info: revealed-type: Revealed type - --> src/mdtest_snippet.py:16:1 - | -15 | # error: [call-non-callable] "Cannot instantiate class `GenericProtocol`" -16 | reveal_type(GenericProtocol[int]()) # revealed: GenericProtocol[int] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `GenericProtocol[int]` -17 | class SubclassOfMyProtocol(MyProtocol): ... - | - -``` - ``` info: revealed-type: Revealed type --> src/mdtest_snippet.py:19:1 diff --git a/crates/ty_test/src/lib.rs b/crates/ty_test/src/lib.rs index d6f26b579b..e0838249f2 100644 --- a/crates/ty_test/src/lib.rs +++ b/crates/ty_test/src/lib.rs @@ -358,6 +358,7 @@ fn run_test( } }; diagnostics.extend(type_diagnostics.into_iter().cloned()); + diagnostics.sort_by(|left, right|left.rendering_sort_key(db).cmp(&right.rendering_sort_key(db))); let failure = match matcher::match_file(db, test_file.file, &diagnostics) { Ok(()) => None,