Skip to content

Commit

Permalink
Add trait_mismatched_generic_lifetimes lint. (#1040)
Browse files Browse the repository at this point in the history
Analogous to #1039, but this time for traits instead of
structs/enums/unions. For traits, the proof of breakage is in trait
bounds where all lifetimes must be explicitly specified.
  • Loading branch information
obi1kenobi authored Dec 11, 2024
1 parent 649ae53 commit aba9737
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 0 deletions.
69 changes: 69 additions & 0 deletions src/lints/trait_mismatched_generic_lifetimes.ron
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
SemverQuery(
id: "trait_mismatched_generic_lifetimes",
human_readable_name: "trait now takes a different number of generic lifetimes",
description: "A trait now takes a different number of generic lifetime parameters, breaking uses of that trait.",
required_update: Major,
lint_level: Deny,
// The cargo SemVer reference only has entries for generic *type* parameters.
// There's no passable place to link to when it comes to specifically lifetime parameters.
reference_link: None,
query: r#"
{
CrateDiff {
baseline {
item {
... on Trait {
visibility_limit @filter(op: "=", value: ["$public"])
name @output
importable_path {
path @tag @output
public_api @filter(op: "=", value: ["$true"])
}
generic_parameter @fold
@transform(op: "count")
@tag(name: "old_lifetimes_count")
@output(name: "old_lifetimes_count") {
... on GenericLifetimeParameter {
old_lifetimes: name @output
}
}
}
}
}
current {
item {
... on Trait {
visibility_limit @filter(op: "=", value: ["$public"]) @output
importable_path {
path @filter(op: "=", value: ["%path"])
public_api @filter(op: "=", value: ["$true"])
}
generic_parameter @fold
@transform(op: "count")
@filter(op: "!=", value: ["%old_lifetimes_count"])
@output(name: "new_lifetimes_count") {
... on GenericLifetimeParameter {
new_lifetimes: name @output
}
}
span_: span @optional {
filename @output
begin_line @output
}
}
}
}
}
}"#,
arguments: {
"public": "public",
"true": true,
},
error_message: "A trait now takes a different number of generic lifetime parameters. Uses of this trait that name the previous number of parameters, such as in trait bounds, will be broken.",
per_result_error_template: Some("trait {{name}} ({{old_lifetimes_count}} -> {{new_lifetimes_count}} lifetime params) in {{span_filename}}:{{span_begin_line}}"),
)
1 change: 1 addition & 0 deletions src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,7 @@ add_lints!(
trait_method_now_doc_hidden,
trait_method_unsafe_added,
trait_method_unsafe_removed,
trait_mismatched_generic_lifetimes,
trait_missing,
trait_must_use_added,
trait_newly_sealed,
Expand Down
7 changes: 7 additions & 0 deletions test_crates/trait_mismatched_generic_lifetimes/new/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
publish = false
name = "trait_mismatched_generic_lifetimes"
version = "0.1.0"
edition = "2021"

[dependencies]
23 changes: 23 additions & 0 deletions test_crates/trait_mismatched_generic_lifetimes/new/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// All these changes are breaking.
//
// The best witness is using the trait in a bound:
// ```
// fn witness<T: Example>() {}
// ```
// where supplying any number other than the exact number of lifetime parameters is an error:
// ```
// |
// 6 | fn witness<T: Example>() {}
// | ^^^^^^^ expected 2 lifetime parameters
// |
// = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
// help: consider making the bound lifetime-generic with a new `'a` lifetime
// ```

pub trait NotGeneric<'a> {}

pub trait GainsLifetimes<'a, 'b> {}

pub trait LosesLifetimes<'a> {}

pub trait StopsBeingGeneric {}
7 changes: 7 additions & 0 deletions test_crates/trait_mismatched_generic_lifetimes/old/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
publish = false
name = "trait_mismatched_generic_lifetimes"
version = "0.1.0"
edition = "2021"

[dependencies]
23 changes: 23 additions & 0 deletions test_crates/trait_mismatched_generic_lifetimes/old/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// All these changes are breaking.
//
// The best witness is using the trait in a bound:
// ```
// fn witness<T: Example>() {}
// ```
// where supplying any number other than the exact number of lifetime parameters is an error:
// ```
// |
// 6 | fn witness<T: Example>() {}
// | ^^^^^^^ expected 2 lifetime parameters
// |
// = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
// help: consider making the bound lifetime-generic with a new `'a` lifetime
// ```

pub trait NotGeneric {}

pub trait GainsLifetimes<'a> {}

pub trait LosesLifetimes<'a, 'b> {}

pub trait StopsBeingGeneric<'a, 'b> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
source: src/query.rs
expression: "&query_execution_results"
snapshot_kind: text
---
{
"./test_crates/trait_mismatched_generic_lifetimes/": [
{
"name": String("NotGeneric"),
"new_lifetimes": List([
String("\'a"),
]),
"new_lifetimes_count": Uint64(1),
"old_lifetimes": List([]),
"old_lifetimes_count": Uint64(0),
"path": List([
String("trait_mismatched_generic_lifetimes"),
String("NotGeneric"),
]),
"span_begin_line": Uint64(17),
"span_filename": String("src/lib.rs"),
"visibility_limit": String("public"),
},
{
"name": String("GainsLifetimes"),
"new_lifetimes": List([
String("\'a"),
String("\'b"),
]),
"new_lifetimes_count": Uint64(2),
"old_lifetimes": List([
String("\'a"),
]),
"old_lifetimes_count": Uint64(1),
"path": List([
String("trait_mismatched_generic_lifetimes"),
String("GainsLifetimes"),
]),
"span_begin_line": Uint64(19),
"span_filename": String("src/lib.rs"),
"visibility_limit": String("public"),
},
{
"name": String("LosesLifetimes"),
"new_lifetimes": List([
String("\'a"),
]),
"new_lifetimes_count": Uint64(1),
"old_lifetimes": List([
String("\'a"),
String("\'b"),
]),
"old_lifetimes_count": Uint64(2),
"path": List([
String("trait_mismatched_generic_lifetimes"),
String("LosesLifetimes"),
]),
"span_begin_line": Uint64(21),
"span_filename": String("src/lib.rs"),
"visibility_limit": String("public"),
},
{
"name": String("StopsBeingGeneric"),
"new_lifetimes": List([]),
"new_lifetimes_count": Uint64(0),
"old_lifetimes": List([
String("\'a"),
String("\'b"),
]),
"old_lifetimes_count": Uint64(2),
"path": List([
String("trait_mismatched_generic_lifetimes"),
String("StopsBeingGeneric"),
]),
"span_begin_line": Uint64(23),
"span_filename": String("src/lib.rs"),
"visibility_limit": String("public"),
},
],
}

0 comments on commit aba9737

Please sign in to comment.