From 3565554e9b61eb671d94589e9b48f4d07fbd696e Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Sat, 21 Dec 2024 18:23:38 -0500 Subject: [PATCH] Add `trait_method_requires_different_const_generic_params` lint. (#1060) Catches cases where trait methods require a different number of const generic parameters than they used to. --- ...equires_different_const_generic_params.ron | 93 +++++++++++++++++++ src/query.rs | 1 + .../new/Cargo.toml | 7 ++ .../new/src/lib.rs | 7 ++ .../old/Cargo.toml | 7 ++ .../old/src/lib.rs | 7 ++ ...quires_different_const_generic_params.snap | 66 +++++++++++++ 7 files changed, 188 insertions(+) create mode 100644 src/lints/trait_method_requires_different_const_generic_params.ron create mode 100644 test_crates/trait_method_requires_different_const_generic_params/new/Cargo.toml create mode 100644 test_crates/trait_method_requires_different_const_generic_params/new/src/lib.rs create mode 100644 test_crates/trait_method_requires_different_const_generic_params/old/Cargo.toml create mode 100644 test_crates/trait_method_requires_different_const_generic_params/old/src/lib.rs create mode 100644 test_outputs/query_execution/trait_method_requires_different_const_generic_params.snap diff --git a/src/lints/trait_method_requires_different_const_generic_params.ron b/src/lints/trait_method_requires_different_const_generic_params.ron new file mode 100644 index 00000000..8792c460 --- /dev/null +++ b/src/lints/trait_method_requires_different_const_generic_params.ron @@ -0,0 +1,93 @@ +SemverQuery( + id: "trait_method_requires_different_const_generic_params", + human_readable_name: "trait 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 trait 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 Trait { + visibility_limit @filter(op: "=", value: ["$public"]) + name @output + + importable_path { + path @tag @output + public_api @filter(op: "=", value: ["$true"]) + } + + method { + public_api_eligible @filter(op: "=", value: ["$true"]) + method: name @output @tag + + 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 + } + } + } + } + } + } + current { + item { + ... on Trait { + visibility_limit @filter(op: "=", value: ["$public"]) @output + + importable_path { + path @filter(op: "=", value: ["%path"]) + public_api @filter(op: "=", value: ["$true"]) + } + + method { + public_api_eligible @filter(op: "=", value: ["$true"]) + name @filter(op: "=", value: ["%method"]) + + generic_parameter @fold + @transform(op: "count") + @filter(op: "!=", value: ["%old_required_const_count"]) + @output(name: "new_required_const_count") { + ... on GenericConstParameter { + new_required_consts: name @output + } + } + + span_: span @optional { + filename @output + begin_line @output + } + } + + } + } + } + } + }"#, + arguments: { + "public": "public", + "true": true, + }, + error_message: "A trait method now requires a different number of const generic parameters than it used to. Calls or implementations of this trait method using the previous number of const generics will be broken.", + per_result_error_template: Some("{{name}}::{{method}} ({{old_required_const_count}} -> {{new_required_const_count}} const generics) in {{span_filename}}:{{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 here depends on whether the trait method is implementable or sealed, + // and whether the method is callable. + // - If the method is callable, the witness would be a trait method invocation + // with the old number of const generics, which is insufficient for the new definition. + // - If the trait is not sealed, and the method is implementable, then the witness would be + // an implementation of that trait that supplies an implementation of the method with the old + // number of const generics. Refinement by making a method implementation more generic isn't + // supported, so this is always breaking in current Rust. + witness: None, +) diff --git a/src/query.rs b/src/query.rs index 088ce54f..b8f1cad7 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1207,6 +1207,7 @@ add_lints!( trait_method_added, trait_method_missing, trait_method_now_doc_hidden, + trait_method_requires_different_const_generic_params, trait_method_unsafe_added, trait_method_unsafe_removed, trait_mismatched_generic_lifetimes, diff --git a/test_crates/trait_method_requires_different_const_generic_params/new/Cargo.toml b/test_crates/trait_method_requires_different_const_generic_params/new/Cargo.toml new file mode 100644 index 00000000..0119872c --- /dev/null +++ b/test_crates/trait_method_requires_different_const_generic_params/new/Cargo.toml @@ -0,0 +1,7 @@ +[package] +publish = false +name = "trait_method_requires_different_const_generic_params" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test_crates/trait_method_requires_different_const_generic_params/new/src/lib.rs b/test_crates/trait_method_requires_different_const_generic_params/new/src/lib.rs new file mode 100644 index 00000000..e5272dd2 --- /dev/null +++ b/test_crates/trait_method_requires_different_const_generic_params/new/src/lib.rs @@ -0,0 +1,7 @@ +pub trait Example { + fn becomes_generic(data: [i64; N]) -> [i64; 4]; + + fn gains_const_generics(data: [i64; N]) -> [i64; M]; + + fn loses_const_generics(data: [i64; N]) -> [i64; 4]; +} diff --git a/test_crates/trait_method_requires_different_const_generic_params/old/Cargo.toml b/test_crates/trait_method_requires_different_const_generic_params/old/Cargo.toml new file mode 100644 index 00000000..0119872c --- /dev/null +++ b/test_crates/trait_method_requires_different_const_generic_params/old/Cargo.toml @@ -0,0 +1,7 @@ +[package] +publish = false +name = "trait_method_requires_different_const_generic_params" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test_crates/trait_method_requires_different_const_generic_params/old/src/lib.rs b/test_crates/trait_method_requires_different_const_generic_params/old/src/lib.rs new file mode 100644 index 00000000..5104ae00 --- /dev/null +++ b/test_crates/trait_method_requires_different_const_generic_params/old/src/lib.rs @@ -0,0 +1,7 @@ +pub trait Example { + fn becomes_generic(data: [i64; 4]) -> [i64; 4]; + + fn gains_const_generics(data: [i64; N]) -> [i64; 4]; + + fn loses_const_generics(data: [i64; N]) -> [i64; M]; +} diff --git a/test_outputs/query_execution/trait_method_requires_different_const_generic_params.snap b/test_outputs/query_execution/trait_method_requires_different_const_generic_params.snap new file mode 100644 index 00000000..3bc173f5 --- /dev/null +++ b/test_outputs/query_execution/trait_method_requires_different_const_generic_params.snap @@ -0,0 +1,66 @@ +--- +source: src/query.rs +expression: "&query_execution_results" +snapshot_kind: text +--- +{ + "./test_crates/trait_method_requires_different_const_generic_params/": [ + { + "method": String("becomes_generic"), + "name": String("Example"), + "new_required_const_count": Uint64(1), + "new_required_consts": List([ + String("N"), + ]), + "old_required_const_count": Uint64(0), + "old_required_consts": List([]), + "path": List([ + String("trait_method_requires_different_const_generic_params"), + String("Example"), + ]), + "span_begin_line": Uint64(2), + "span_filename": String("src/lib.rs"), + "visibility_limit": String("public"), + }, + { + "method": String("gains_const_generics"), + "name": String("Example"), + "new_required_const_count": Uint64(2), + "new_required_consts": List([ + String("N"), + String("M"), + ]), + "old_required_const_count": Uint64(1), + "old_required_consts": List([ + String("N"), + ]), + "path": List([ + String("trait_method_requires_different_const_generic_params"), + String("Example"), + ]), + "span_begin_line": Uint64(4), + "span_filename": String("src/lib.rs"), + "visibility_limit": String("public"), + }, + { + "method": String("loses_const_generics"), + "name": String("Example"), + "new_required_const_count": Uint64(1), + "new_required_consts": List([ + String("N"), + ]), + "old_required_const_count": Uint64(2), + "old_required_consts": List([ + String("N"), + String("M"), + ]), + "path": List([ + String("trait_method_requires_different_const_generic_params"), + String("Example"), + ]), + "span_begin_line": Uint64(6), + "span_filename": String("src/lib.rs"), + "visibility_limit": String("public"), + }, + ], +}