-
-
Notifications
You must be signed in to change notification settings - Fork 79
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
method_requires_different_const_generic_params
lint. (#1058)
It catches methods that have a different number of const generic parameters than they used to.
- Loading branch information
1 parent
f88e0bd
commit 127ba43
Showing
7 changed files
with
282 additions
and
0 deletions.
There are no files selected for viewing
142 changes: 142 additions & 0 deletions
142
src/lints/method_requires_different_const_generic_params.ron
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
SemverQuery( | ||
id: "method_requires_different_const_generic_params", | ||
human_readable_name: "method now requires a different number of const generic parameters", | ||
// Currently, const generics in functions and methods cannot have defaults set. | ||
// This is why we have only one lint ("requires different number") instead of | ||
// two separate lints ("requires" / "allows") like for structs/traits etc. | ||
description: "A method now requires a different number of const generic parameters than before.", | ||
required_update: Major, | ||
lint_level: Deny, | ||
reference_link: Some("https://doc.rust-lang.org/reference/items/generics.html#const-generics"), | ||
query: r#" | ||
{ | ||
CrateDiff { | ||
baseline { | ||
item { | ||
... on ImplOwner { | ||
visibility_limit @filter(op: "=", value: ["$public"]) | ||
importable_path { | ||
path @tag @output | ||
public_api @filter(op: "=", value: ["$true"]) | ||
} | ||
inherent_impl { | ||
method { | ||
method_visibility: visibility_limit @filter(op: "=", value: ["$public"]) @output | ||
method_name: name @output @tag | ||
public_api_eligible @filter(op: "=", value: ["$true"]) | ||
generic_parameter @fold | ||
@transform(op: "count") | ||
@tag(name: "old_required_const_count") | ||
@output(name: "old_required_const_count") { | ||
... on GenericConstParameter { | ||
old_required_consts: name @output | ||
} | ||
} | ||
span_: span @optional { | ||
filename @output | ||
begin_line @output | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
current { | ||
item { | ||
... on ImplOwner { | ||
visibility_limit @filter(op: "=", value: ["$public"]) @output | ||
name @output | ||
importable_path { | ||
path @filter(op: "=", value: ["%path"]) | ||
public_api @filter(op: "=", value: ["$true"]) | ||
} | ||
# We use "impl" instead of "inherent_impl" here because moving | ||
# an inherently-implemented method to a trait is not necessarily | ||
# a breaking change, but changing the number of const generics is. | ||
# | ||
# Method names are not unique on an ImplOwner! It's perfectly valid to have | ||
# both an inherent method `foo()` as well as a trait-provided method | ||
# `<Self as Bar>::foo()` at the same time. Whenever possible, rustc | ||
# attempts to "do the right thing" and dispatch to the correct one. | ||
# | ||
# Because of the above, this check has to be written as | ||
# "there is no method with the correct name and number of const generics" | ||
# rather than the (incorrect!) alternative | ||
# "the named method does not have the correct number of arguments." | ||
# | ||
# The above by itself is still not enough: say if the method was removed, | ||
# that still makes the "there is no method ..." statement true. | ||
# So we add an additional clause demanding that a method by that name | ||
# with appropriate visibility actually exists. | ||
impl @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { | ||
method { | ||
visibility_limit @filter(op: "one_of", value: ["$public_or_default"]) | ||
name @filter(op: "=", value: ["%method_name"]) | ||
public_api_eligible @filter(op: "=", value: ["$true"]) | ||
} | ||
} | ||
impl @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { | ||
method { | ||
visibility_limit @filter(op: "one_of", value: ["$public_or_default"]) | ||
name @filter(op: "=", value: ["%method_name"]) | ||
public_api_eligible @filter(op: "=", value: ["$true"]) | ||
generic_parameter @fold | ||
@transform(op: "count") | ||
@filter(op: "=", value: ["%old_required_const_count"]) { | ||
... on GenericConstParameter { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
# Get the non-matching methods by that name so we can report them | ||
# in the lint error message. | ||
impl @fold { | ||
method { | ||
visibility_limit @filter(op: "one_of", value: ["$public_or_default"]) | ||
name @filter(op: "=", value: ["%method_name"]) | ||
public_api_eligible @filter(op: "=", value: ["$true"]) | ||
generic_parameter @fold | ||
@transform(op: "count") | ||
@output(name: "new_required_const_count") { | ||
... on GenericConstParameter { | ||
new_required_consts: name @output | ||
} | ||
} | ||
non_matching_span_: span @optional { | ||
filename @output | ||
begin_line @output | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}"#, | ||
arguments: { | ||
"public": "public", | ||
"public_or_default": ["public", "default"], | ||
"true": true, | ||
"zero": 0, | ||
}, | ||
error_message: "A method now requires a different number of const generic parameters than it used to. Uses of this method that supplied the previous number of const generics will be broken.", | ||
per_result_error_template: Some("{{join \"::\" path}}::{{method_name}} takes {{unpack_if_singleton new_required_const_count}} const generics instead of {{old_required_const_count}}, in {{multiple_spans non_matching_span_filename non_matching_span_begin_line}}"), | ||
// TODO: see https://github.com/obi1kenobi/cargo-semver-checks/blob/main/CONTRIBUTING.md#adding-a-witness | ||
// for information about this field. | ||
// | ||
// The witness would be a invocation with the old number | ||
// of const generics, which is insufficient for the new definition. | ||
witness: None, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
test_crates/method_requires_different_const_generic_params/new/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[package] | ||
publish = false | ||
name = "method_requires_different_const_generic_params" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] |
15 changes: 15 additions & 0 deletions
15
test_crates/method_requires_different_const_generic_params/new/src/lib.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
pub struct Owner; | ||
|
||
impl Owner { | ||
pub fn previously_not_generic<const N: usize>() -> [i64; N] { | ||
todo!() | ||
} | ||
|
||
pub fn added_const_generic<const N: usize, const M: usize>(data: [i64; N]) -> [i64; M] { | ||
todo!() | ||
} | ||
|
||
pub fn removed_const_generic<const N: usize>(data: [i64; N]) -> [i64; 4] { | ||
todo!() | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
test_crates/method_requires_different_const_generic_params/old/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[package] | ||
publish = false | ||
name = "method_requires_different_const_generic_params" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] |
11 changes: 11 additions & 0 deletions
11
test_crates/method_requires_different_const_generic_params/old/src/lib.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
pub struct Owner; | ||
|
||
impl Owner { | ||
pub fn previously_not_generic() {} | ||
|
||
pub fn added_const_generic<const N: usize>(data: [i64; N]) {} | ||
|
||
pub fn removed_const_generic<const N: usize, const M: usize>(data: [i64; N]) -> [i64; M] { | ||
todo!() | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
test_outputs/query_execution/method_requires_different_const_generic_params.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
--- | ||
source: src/query.rs | ||
expression: "&query_execution_results" | ||
snapshot_kind: text | ||
--- | ||
{ | ||
"./test_crates/method_requires_different_const_generic_params/": [ | ||
{ | ||
"method_name": String("previously_not_generic"), | ||
"method_visibility": String("public"), | ||
"name": String("Owner"), | ||
"new_required_const_count": List([ | ||
Uint64(1), | ||
]), | ||
"new_required_consts": List([ | ||
List([ | ||
String("N"), | ||
]), | ||
]), | ||
"non_matching_span_begin_line": List([ | ||
Uint64(4), | ||
]), | ||
"non_matching_span_filename": List([ | ||
String("src/lib.rs"), | ||
]), | ||
"old_required_const_count": Uint64(0), | ||
"old_required_consts": List([]), | ||
"path": List([ | ||
String("method_requires_different_const_generic_params"), | ||
String("Owner"), | ||
]), | ||
"span_begin_line": Uint64(4), | ||
"span_filename": String("src/lib.rs"), | ||
"visibility_limit": String("public"), | ||
}, | ||
{ | ||
"method_name": String("added_const_generic"), | ||
"method_visibility": String("public"), | ||
"name": String("Owner"), | ||
"new_required_const_count": List([ | ||
Uint64(2), | ||
]), | ||
"new_required_consts": List([ | ||
List([ | ||
String("N"), | ||
String("M"), | ||
]), | ||
]), | ||
"non_matching_span_begin_line": List([ | ||
Uint64(8), | ||
]), | ||
"non_matching_span_filename": List([ | ||
String("src/lib.rs"), | ||
]), | ||
"old_required_const_count": Uint64(1), | ||
"old_required_consts": List([ | ||
String("N"), | ||
]), | ||
"path": List([ | ||
String("method_requires_different_const_generic_params"), | ||
String("Owner"), | ||
]), | ||
"span_begin_line": Uint64(6), | ||
"span_filename": String("src/lib.rs"), | ||
"visibility_limit": String("public"), | ||
}, | ||
{ | ||
"method_name": String("removed_const_generic"), | ||
"method_visibility": String("public"), | ||
"name": String("Owner"), | ||
"new_required_const_count": List([ | ||
Uint64(1), | ||
]), | ||
"new_required_consts": List([ | ||
List([ | ||
String("N"), | ||
]), | ||
]), | ||
"non_matching_span_begin_line": List([ | ||
Uint64(12), | ||
]), | ||
"non_matching_span_filename": List([ | ||
String("src/lib.rs"), | ||
]), | ||
"old_required_const_count": Uint64(2), | ||
"old_required_consts": List([ | ||
String("N"), | ||
String("M"), | ||
]), | ||
"path": List([ | ||
String("method_requires_different_const_generic_params"), | ||
String("Owner"), | ||
]), | ||
"span_begin_line": Uint64(8), | ||
"span_filename": String("src/lib.rs"), | ||
"visibility_limit": String("public"), | ||
}, | ||
], | ||
} |