Skip to content

Commit fe005eb

Browse files
authored
Merge branch 'main' into unconditionally-sealed-trait-became-unsealed
2 parents 1232ade + af24e84 commit fe005eb

21 files changed

+1713
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
SemverQuery(
2+
id: "partial_ord_enum_struct_variant_fields_reordered",
3+
human_readable_name: "struct variant fields reordered in #[derive(PartialOrd)] enum",
4+
description: "A public enum that derives PartialOrd had fields reordered within a struct variant, which changes its ordering behavior.",
5+
required_update: Major,
6+
lint_level: Warn,
7+
reference_link: Some("https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html#derivable"),
8+
query: r#"
9+
{
10+
CrateDiff {
11+
baseline {
12+
item {
13+
... on Enum {
14+
visibility_limit @filter(op: "=", value: ["$public"])
15+
16+
impl {
17+
negative @filter(op: "=", value: ["$false"])
18+
attrs @filter(op: "contains", value: ["$derived"])
19+
20+
implemented_trait {
21+
trait {
22+
canonical_path {
23+
path @filter(op: "=", value: ["$partial_ord"])
24+
}
25+
}
26+
}
27+
}
28+
29+
importable_path {
30+
path @tag @output
31+
public_api @filter(op: "=", value: ["$true"])
32+
}
33+
34+
variant {
35+
... on StructVariant {
36+
variant_name: name @output @tag
37+
public_api_eligible @filter(op: "=", value: ["$true"])
38+
39+
field {
40+
field_name: name @output @tag
41+
public_api_eligible @filter(op: "=", value: ["$true"])
42+
position @output @tag
43+
}
44+
}
45+
}
46+
}
47+
}
48+
}
49+
current {
50+
item {
51+
... on Enum {
52+
visibility_limit @filter(op: "=", value: ["$public"])
53+
name @output
54+
55+
impl {
56+
negative @filter(op: "=", value: ["$false"])
57+
attrs @filter(op: "contains", value: ["$derived"])
58+
59+
implemented_trait {
60+
trait {
61+
canonical_path {
62+
path @filter(op: "=", value: ["$partial_ord"])
63+
}
64+
}
65+
}
66+
}
67+
68+
importable_path {
69+
path @filter(op: "=", value: ["%path"])
70+
public_api @filter(op: "=", value: ["$true"])
71+
}
72+
73+
variant {
74+
... on StructVariant {
75+
name @filter(op: "=", value: ["%variant_name"])
76+
public_api_eligible @filter(op: "=", value: ["$true"])
77+
78+
field {
79+
name @filter(op: "=", value: ["%field_name"])
80+
public_api_eligible @filter(op: "=", value: ["$true"])
81+
position @filter(op: "!=", value: ["%position"]) @output(name: "new_position")
82+
span_: span @optional {
83+
filename @output
84+
begin_line @output
85+
end_line @output
86+
}
87+
}
88+
}
89+
}
90+
}
91+
}
92+
}
93+
}
94+
}"#,
95+
arguments: {
96+
"public": "public",
97+
"derived": "#[automatically_derived]",
98+
"false": false,
99+
"true": true,
100+
"partial_ord": ["core", "cmp", "PartialOrd"],
101+
},
102+
error_message: "A public enum that derives PartialOrd had fields reordered within a struct variant. #[derive(PartialOrd)] uses field order to determine ordering behavior, so this change may break downstream code that relies on the previous ordering.",
103+
per_result_error_template: Some("{{name}}::{{variant_name}}.{{field_name}} moved from position {{position}} to {{new_position}}, in {{span_filename}}:{{span_begin_line}}"),
104+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
SemverQuery(
2+
id: "partial_ord_enum_variants_reordered",
3+
human_readable_name: "enum variants reordered in #[derive(PartialOrd)] enum",
4+
description: "A public enum that derives PartialOrd had its variants reordered, which changes its ordering behavior.",
5+
required_update: Major,
6+
lint_level: Warn,
7+
reference_link: Some("https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html#derivable"),
8+
query: r#"
9+
{
10+
CrateDiff {
11+
baseline {
12+
item {
13+
... on Enum {
14+
visibility_limit @filter(op: "=", value: ["$public"])
15+
16+
impl {
17+
negative @filter(op: "=", value: ["$false"])
18+
attrs @filter(op: "contains", value: ["$derived"])
19+
20+
implemented_trait {
21+
trait {
22+
canonical_path {
23+
path @filter(op: "=", value: ["$partial_ord"])
24+
}
25+
}
26+
}
27+
}
28+
29+
importable_path {
30+
path @tag @output
31+
public_api @filter(op: "=", value: ["$true"])
32+
}
33+
34+
variant {
35+
variant_name: name @output @tag
36+
public_api_eligible @filter(op: "=", value: ["$true"])
37+
position @output @tag
38+
}
39+
}
40+
}
41+
}
42+
current {
43+
item {
44+
... on Enum {
45+
visibility_limit @filter(op: "=", value: ["$public"])
46+
name @output
47+
48+
impl {
49+
negative @filter(op: "=", value: ["$false"])
50+
attrs @filter(op: "contains", value: ["$derived"])
51+
52+
implemented_trait {
53+
trait {
54+
canonical_path {
55+
path @filter(op: "=", value: ["$partial_ord"])
56+
}
57+
}
58+
}
59+
}
60+
61+
importable_path {
62+
path @filter(op: "=", value: ["%path"])
63+
public_api @filter(op: "=", value: ["$true"])
64+
}
65+
66+
variant {
67+
name @filter(op: "=", value: ["%variant_name"])
68+
public_api_eligible @filter(op: "=", value: ["$true"])
69+
position @filter(op: "!=", value: ["%position"]) @output(name: "new_position")
70+
71+
span_: span @optional {
72+
filename @output
73+
begin_line @output
74+
end_line @output
75+
}
76+
}
77+
}
78+
}
79+
}
80+
}
81+
}"#,
82+
arguments: {
83+
"public": "public",
84+
"derived": "#[automatically_derived]",
85+
"false": false,
86+
"true": true,
87+
"partial_ord": ["core", "cmp", "PartialOrd"],
88+
},
89+
error_message: "A public enum that derives PartialOrd had its variants reordered. #[derive(PartialOrd)] uses the enum variant order to set the enum's ordering behavior, so this change may break downstream code that relies on the previous order.",
90+
per_result_error_template: Some("{{name}}::{{variant_name}} moved from position {{position}} to {{new_position}}, in {{span_filename}}:{{span_begin_line}}"),
91+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
SemverQuery(
2+
id: "pub_api_sealed_trait_became_unsealed",
3+
human_readable_name: "public API sealed trait became unsealed",
4+
description: "A public API sealed trait has become unsealed, allowing downstream crates to implement it. Reverting this would be a major breaking change.",
5+
required_update: Minor,
6+
lint_level: Warn,
7+
reference_link: Some("https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed"),
8+
query: r#"
9+
{
10+
CrateDiff {
11+
baseline {
12+
item {
13+
... on Trait {
14+
visibility_limit @filter(op: "=", value: ["$public"]) @output
15+
public_api_sealed @filter(op: "=", value: ["$true"])
16+
unconditionally_sealed @filter(op: "!=", value: ["$true"])
17+
18+
importable_path {
19+
path @output @tag
20+
public_api @filter(op: "=", value: ["$true"])
21+
}
22+
}
23+
}
24+
}
25+
current {
26+
item {
27+
... on Trait {
28+
visibility_limit @filter(op: "=", value: ["$public"])
29+
public_api_sealed @filter(op: "!=", value: ["$true"])
30+
name @output
31+
32+
importable_path {
33+
path @filter(op: "=", value: ["%path"])
34+
public_api @filter(op: "=", value: ["$true"])
35+
}
36+
37+
span_: span @optional {
38+
filename @output
39+
begin_line @output
40+
end_line @output
41+
}
42+
}
43+
}
44+
}
45+
}
46+
}"#,
47+
arguments: {
48+
"public": "public",
49+
"true": true,
50+
},
51+
error_message: "A public API sealed trait has become unsealed, allowing downstream crates to implement it. Reverting this would be a major breaking change.",
52+
per_result_error_template: Some("trait {{join \"::\" path}} in file {{span_filename}}:{{span_begin_line}}"),
53+
witness: None,
54+
)

src/query.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,8 @@ add_lints!(
12821282
method_requires_different_generic_type_params,
12831283
module_missing,
12841284
non_exhaustive_struct_changed_type,
1285+
partial_ord_enum_struct_variant_fields_reordered,
1286+
partial_ord_enum_variants_reordered,
12851287
partial_ord_struct_fields_reordered,
12861288
proc_macro_marked_deprecated,
12871289
proc_macro_now_doc_hidden,
@@ -1341,6 +1343,7 @@ add_lints!(
13411343
trait_removed_supertrait,
13421344
trait_requires_more_const_generic_params,
13431345
trait_requires_more_generic_type_params,
1346+
pub_api_sealed_trait_became_unsealed,
13441347
trait_unsafe_added,
13451348
trait_unsafe_removed,
13461349
tuple_struct_to_plain_struct,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
publish = false
3+
name = "partial_ord_enum_struct_variant_fields_reordered"
4+
version = "0.1.0"
5+
edition = "2021"
6+
7+
[dependencies]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Public enum with #[derive(PartialOrd)] and struct variant with reordered fields - should trigger warning
2+
#[derive(PartialOrd, PartialEq)]
3+
pub enum PublicEnum {
4+
StructVariant { b: u16, c: u32, a: u8 },
5+
}
6+
7+
// Private enum with #[derive(PartialOrd)] and struct variant with reordered fields - should not trigger
8+
#[derive(PartialOrd, PartialEq)]
9+
enum PrivateEnum {
10+
StructVariant { b: u16, c: u32, a: u8 },
11+
}
12+
13+
// Public enum without #[derive(PartialOrd)] but with struct variant with reordered fields - should not trigger
14+
pub enum RegularEnum {
15+
StructVariant { b: u16, c: u32, a: u8 },
16+
}
17+
18+
// Public enum with #[derive(PartialOrd)] and doc(hidden) - should not trigger
19+
#[doc(hidden)]
20+
#[derive(PartialOrd, PartialEq)]
21+
pub enum DocHiddenEnum {
22+
StructVariant { b: u16, c: u32, a: u8 },
23+
}
24+
25+
// Public enum with #[derive(PartialOrd)] and doc(hidden) variant - should not trigger
26+
#[derive(PartialOrd, PartialEq)]
27+
pub enum EnumWithDocHiddenVariant {
28+
#[doc(hidden)]
29+
StructVariant { b: u16, c: u32, a: u8 },
30+
}
31+
32+
// Public enum with #[derive(PartialOrd)] and doc(hidden) fields - should not trigger
33+
#[derive(PartialOrd, PartialEq)]
34+
pub enum EnumWithDocHiddenFields {
35+
StructVariant {
36+
a: u8,
37+
#[doc(hidden)]
38+
c: u32,
39+
#[doc(hidden)]
40+
b: u16,
41+
},
42+
}
43+
44+
// Public enum with #[derive(PartialOrd)] and same field order - should not trigger
45+
#[derive(PartialOrd, PartialEq)]
46+
pub enum UnchangedEnum {
47+
StructVariant { x: u8, y: u16 },
48+
}
49+
50+
// Test with manual PartialOrd implementation - should not trigger
51+
#[derive(PartialEq)]
52+
pub enum ManuallyImplemented {
53+
StructVariant { b: u16, a: u8 },
54+
}
55+
56+
impl PartialOrd for ManuallyImplemented {
57+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
58+
todo!()
59+
}
60+
}
61+
62+
// Test with multiple variants - should check each struct variant independently
63+
#[derive(PartialOrd, PartialEq)]
64+
pub enum MultipleVariants {
65+
First { b: u16, a: u8 },
66+
Second { y: u64, x: u32 },
67+
Third(u16, u8), // tuple variant reordered but should be ignored
68+
Fourth, // unit variant unchanged
69+
}
70+
71+
// Test case: Used to derive PartialOrd but no longer does - should not trigger this lint
72+
#[derive(PartialEq)]
73+
pub enum NoLongerPartialOrd {
74+
StructVariant { b: u16, c: u32, a: u8 },
75+
}
76+
77+
// Test case: Didn't derive PartialOrd before but now does - should not trigger this lint
78+
#[derive(PartialOrd, PartialEq)]
79+
pub enum NewlyPartialOrd {
80+
StructVariant { b: u16, c: u32, a: u8 },
81+
}
82+
83+
// Test case: Switching from derived to hand-implemented PartialOrd - should not trigger this lint
84+
#[derive(PartialEq)]
85+
pub enum DerivedToHandImpl {
86+
StructVariant { b: u16, c: u32, a: u8 },
87+
}
88+
89+
impl PartialOrd for DerivedToHandImpl {
90+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
91+
todo!()
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
publish = false
3+
name = "partial_ord_enum_struct_variant_fields_reordered"
4+
version = "0.1.0"
5+
edition = "2021"
6+
7+
[dependencies]

0 commit comments

Comments
 (0)