Skip to content

Commit 1bd77e1

Browse files
authored
[ty] Use diagnostic message as tie breaker when sorting (#25424)
1 parent 7e1bc1e commit 1bd77e1

5 files changed

Lines changed: 69 additions & 34 deletions

File tree

crates/ruff_db/src/diagnostic/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ impl Ord for RenderingSortKey<'_> {
551551
// We sort diagnostics in a way that keeps them in source order
552552
// and grouped by file. After that, we fall back to severity
553553
// (with fatal messages sorting before info messages) and then
554-
// finally the diagnostic ID.
554+
// finally the diagnostic ID and concise message.
555555
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
556556
if let (Some(span1), Some(span2)) = (
557557
self.diagnostic.primary_span(),
@@ -578,7 +578,15 @@ impl Ord for RenderingSortKey<'_> {
578578
if order.is_ne() {
579579
return order;
580580
}
581-
self.diagnostic.id().cmp(&other.diagnostic.id())
581+
let order = self.diagnostic.id().cmp(&other.diagnostic.id());
582+
if order.is_ne() {
583+
return order;
584+
}
585+
586+
self.diagnostic
587+
.concise_message()
588+
.to_str()
589+
.cmp(&other.diagnostic.concise_message().to_str())
582590
}
583591
}
584592

crates/ruff_db/src/diagnostic/render.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2539,6 +2539,33 @@ watermelon
25392539
);
25402540
}
25412541

2542+
#[test]
2543+
fn diagnostics_with_equal_locations_sort_by_concise_message() {
2544+
let mut env = TestEnvironment::new();
2545+
env.add("fruits", FRUITS);
2546+
let mut diagnostics = [
2547+
env.invalid_syntax("checking mod.py")
2548+
.primary("fruits", "1", "1", "")
2549+
.build(),
2550+
env.invalid_syntax("checking main.py")
2551+
.primary("fruits", "1", "1", "")
2552+
.build(),
2553+
];
2554+
2555+
diagnostics.sort_by(|left, right| {
2556+
left.rendering_sort_key(&env.db)
2557+
.cmp(&right.rendering_sort_key(&env.db))
2558+
});
2559+
2560+
assert_eq!(
2561+
diagnostics
2562+
.iter()
2563+
.map(Diagnostic::primary_message)
2564+
.collect::<Vec<_>>(),
2565+
["checking main.py", "checking mod.py"]
2566+
);
2567+
}
2568+
25422569
/// A small harness for setting up an environment specifically for testing
25432570
/// diagnostic rendering.
25442571
pub(super) struct TestEnvironment {

crates/ty_python_semantic/resources/mdtest/call/union.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,22 @@ def test(f: Intersection[IntCaller, StrCaller] | BytesCaller):
935935
```
936936

937937
```snapshot
938+
error[invalid-argument-type]: Argument to bound method `BytesCaller.__call__` is incorrect
939+
--> src/mdtest_snippet.py:21:7
940+
|
941+
21 | f(None)
942+
| ^^^^ Expected `bytes`, found `None`
943+
|
944+
info: Method defined here
945+
--> src/mdtest_snippet.py:13:9
946+
|
947+
13 | def __call__(self, x: bytes) -> bytes:
948+
| ^^^^^^^^ -------- Parameter declared here
949+
|
950+
info: Union variant `BytesCaller` is incompatible with this call site
951+
info: Attempted to call union type `(IntCaller & StrCaller) | BytesCaller`
952+
953+
938954
error[invalid-argument-type]: Argument to bound method `IntCaller.__call__` is incorrect
939955
--> src/mdtest_snippet.py:21:7
940956
|
@@ -952,22 +968,6 @@ info: Attempted to call intersection type `IntCaller & StrCaller`
952968
info: Attempted to call union type `(IntCaller & StrCaller) | BytesCaller`
953969
954970
955-
error[invalid-argument-type]: Argument to bound method `BytesCaller.__call__` is incorrect
956-
--> src/mdtest_snippet.py:21:7
957-
|
958-
21 | f(None)
959-
| ^^^^ Expected `bytes`, found `None`
960-
|
961-
info: Method defined here
962-
--> src/mdtest_snippet.py:13:9
963-
|
964-
13 | def __call__(self, x: bytes) -> bytes:
965-
| ^^^^^^^^ -------- Parameter declared here
966-
|
967-
info: Union variant `BytesCaller` is incompatible with this call site
968-
info: Attempted to call union type `(IntCaller & StrCaller) | BytesCaller`
969-
970-
971971
error[invalid-argument-type]: Argument to bound method `StrCaller.__call__` is incorrect
972972
--> src/mdtest_snippet.py:21:7
973973
|

crates/ty_python_semantic/resources/mdtest/snapshots/assignment_diagnosti…_-_Subscript_assignment…_-_Unknown_key_for_all_…_(8a0f0e8ceccc51b2).snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
source: crates/ty_test/src/lib.rs
2+
source: crates/mdtest/src/lib.rs
33
expression: snapshot
44
---
55

@@ -32,25 +32,25 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/subscript/assignment_dia
3232
# Diagnostics
3333

3434
```
35-
error[invalid-key]: Unknown key "nane" for TypedDict `Person`
35+
error[invalid-key]: Unknown key "nane" for TypedDict `Animal`
3636
--> src/mdtest_snippet.py:14:5
3737
|
3838
14 | being["nane"] = "unknown"
3939
| ----- ^^^^^^ Unknown key "nane"
4040
| |
41-
| TypedDict `Person` in union type `Person | Animal`
41+
| TypedDict `Animal` in union type `Person | Animal`
4242
|
4343
4444
```
4545

4646
```
47-
error[invalid-key]: Unknown key "nane" for TypedDict `Animal`
47+
error[invalid-key]: Unknown key "nane" for TypedDict `Person`
4848
--> src/mdtest_snippet.py:14:5
4949
|
5050
14 | being["nane"] = "unknown"
5151
| ----- ^^^^^^ Unknown key "nane"
5252
| |
53-
| TypedDict `Animal` in union type `Person | Animal`
53+
| TypedDict `Person` in union type `Person | Animal`
5454
|
5555
5656
```

crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_lists_wi…_(ea7ebc83ec359b54).snap

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
source: crates/ty_test/src/lib.rs
2+
source: crates/mdtest/src/lib.rs
33
expression: snapshot
44
---
55

@@ -119,7 +119,7 @@ info: The definition of class `Foo` will raise `TypeError` at runtime
119119
```
120120
121121
```
122-
error[duplicate-base]: Duplicate base class `Spam`
122+
error[duplicate-base]: Duplicate base class `Eggs`
123123
--> src/mdtest_snippet.py:16:7
124124
|
125125
16 | class Ham(
@@ -134,21 +134,21 @@ error[duplicate-base]: Duplicate base class `Spam`
134134
| |_^
135135
|
136136
info: The definition of class `Ham` will raise `TypeError` at runtime
137-
--> src/mdtest_snippet.py:17:5
137+
--> src/mdtest_snippet.py:18:5
138138
|
139-
17 | Spam,
140-
| ---- Class `Spam` first included in bases list here
141139
18 | Eggs,
140+
| ---- Class `Eggs` first included in bases list here
142141
19 | Bar,
143142
20 | Baz,
144143
21 | Spam,
145-
| ^^^^ Class `Spam` later repeated here
144+
22 | Eggs,
145+
| ^^^^ Class `Eggs` later repeated here
146146
|
147147
148148
```
149149
150150
```
151-
error[duplicate-base]: Duplicate base class `Eggs`
151+
error[duplicate-base]: Duplicate base class `Spam`
152152
--> src/mdtest_snippet.py:16:7
153153
|
154154
16 | class Ham(
@@ -163,15 +163,15 @@ error[duplicate-base]: Duplicate base class `Eggs`
163163
| |_^
164164
|
165165
info: The definition of class `Ham` will raise `TypeError` at runtime
166-
--> src/mdtest_snippet.py:18:5
166+
--> src/mdtest_snippet.py:17:5
167167
|
168+
17 | Spam,
169+
| ---- Class `Spam` first included in bases list here
168170
18 | Eggs,
169-
| ---- Class `Eggs` first included in bases list here
170171
19 | Bar,
171172
20 | Baz,
172173
21 | Spam,
173-
22 | Eggs,
174-
| ^^^^ Class `Eggs` later repeated here
174+
| ^^^^ Class `Spam` later repeated here
175175
|
176176
177177
```

0 commit comments

Comments
 (0)