diff --git a/src/lints/inherent_associated_pub_const_missing.ron b/src/lints/inherent_associated_pub_const_missing.ron new file mode 100644 index 00000000..011a27f3 --- /dev/null +++ b/src/lints/inherent_associated_pub_const_missing.ron @@ -0,0 +1,74 @@ +SemverQuery( + id: "inherent_associated_pub_const_missing", + human_readable_name: "inherent impl's associated pub const removed", + description: "An inherent impl's associated public const removed or renamed", + required_update: Major, + reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), + query: r#" + { + CrateDiff { + baseline { + item { + ... on ImplOwner { + visibility_limit @filter(op: "=", value: ["$public"]) @output + + importable_path { + path @output @tag + public_api @filter(op: "=", value: ["$true"]) + } + + inherent_impl { + public_api_eligible @filter(op: "=", value: ["$true"]) + + associated_constant { + associated_constant: name @output @tag + public_api_eligible @filter(op: "=", value: ["$true"]) + + span_: span @optional { + filename @output + begin_line @output + } + } + } + } + } + } + current { + item { + ... on ImplOwner { + visibility_limit @filter(op: "=", value: ["$public"]) + name @output + + importable_path { + path @filter(op: "=", value: ["%path"]) + public_api @filter(op: "=", value: ["$true"]) + } + + inherent_impl @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { + associated_constant { + name @filter(op: "=", value: ["%associated_constant"]) + } + } + + impl @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { + implemented_trait { + trait { + associated_constant { + name @filter(op: "=", value: ["%associated_constant"]) + } + } + } + } + } + } + } + } + }"#, + arguments: { + "public": "public", + "true": true, + "zero": 0, + }, + error_message: "An inherent impl's associated public constant is removed or renamed", + per_result_error_template: Some("{{name}}::{{associated_constant}}, previously at {{span_filename}}:{{span_begin_line}}"), +) diff --git a/src/query.rs b/src/query.rs index fa90ba3d..4fa10a6e 100644 --- a/src/query.rs +++ b/src/query.rs @@ -506,6 +506,7 @@ add_lints!( function_now_doc_hidden, function_parameter_count_changed, function_unsafe_added, + inherent_associated_pub_const_missing, inherent_method_const_removed, inherent_method_missing, inherent_method_must_use_added, diff --git a/test_crates/inherent_associated_pub_const_missing/new/Cargo.toml b/test_crates/inherent_associated_pub_const_missing/new/Cargo.toml new file mode 100644 index 00000000..7a2749e2 --- /dev/null +++ b/test_crates/inherent_associated_pub_const_missing/new/Cargo.toml @@ -0,0 +1,7 @@ +[package] +publish = false +name = "inherent_associated_pub_const_missing" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test_crates/inherent_associated_pub_const_missing/new/src/lib.rs b/test_crates/inherent_associated_pub_const_missing/new/src/lib.rs new file mode 100644 index 00000000..dcf65622 --- /dev/null +++ b/test_crates/inherent_associated_pub_const_missing/new/src/lib.rs @@ -0,0 +1,76 @@ +// Test Cases where #[doc(hidden)] is neither added or removed +pub struct StructA {} + +impl StructA { + pub const PublicConstantB: i32 = 0; + // Should Be caught on renaming + pub const PublicConstantRenamedA: i32 = 0; +} + +struct StructB {} + +impl StructB { + // Should not be caught since StructB is not pub + pub const PublicConstantRenamedB: i32 = 0; +} + +#[doc(hidden)] +pub struct StructC {} + +impl StructC { + // Should not be caught on removing or renaming since the struct is #[doc(hidden)] + pub const PublicConstantRenamedC: i32 = 0; +} + +pub struct StructD {} + +#[doc(hidden)] +impl StructD { + pub const PublicConstantE: i32 = 0; +} + +// Test Cases where #[doc(hidden)] is added +pub struct StructE {} + +#[doc(hidden)] +impl StructE { + pub const PublicConstantH: i32 = 0; +} + +#[doc(hidden)] +pub struct StructF {} + +impl StructF { + pub const PublicConstantJ: i32 = 0; +} + +// Test cases where #[doc(hidden)] is removed +pub struct DocHiddenStruct {} + +impl DocHiddenStruct { + pub const PublicConstantL: i32 = 0; +} + +impl DocHiddenStruct { + pub const PublicConstantO: i32 = 0; +} + +// Test for when an inherent const is implemented as trait's const +pub trait TraitA { + const N_A: i64 = 0; +} + +pub struct ExampleA; + +impl TraitA for ExampleA {} + +// Test for when an inherent const is implemented as trait's const but given a value in impl block +pub trait TraitB { + const N_B: i64; +} + +pub struct ExampleB; + +impl TraitB for ExampleB { + const N_B: i64 = 0; +} diff --git a/test_crates/inherent_associated_pub_const_missing/old/Cargo.toml b/test_crates/inherent_associated_pub_const_missing/old/Cargo.toml new file mode 100644 index 00000000..7a2749e2 --- /dev/null +++ b/test_crates/inherent_associated_pub_const_missing/old/Cargo.toml @@ -0,0 +1,7 @@ +[package] +publish = false +name = "inherent_associated_pub_const_missing" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test_crates/inherent_associated_pub_const_missing/old/src/lib.rs b/test_crates/inherent_associated_pub_const_missing/old/src/lib.rs new file mode 100644 index 00000000..3ee0834c --- /dev/null +++ b/test_crates/inherent_associated_pub_const_missing/old/src/lib.rs @@ -0,0 +1,104 @@ +// Test Cases where #[doc(hidden)] is neither added or removed +pub struct StructA {} + +impl StructA { + // Basic Test case should be caught + pub const PublicConstantA: i32 = 0; + pub const PublicConstantB: i32 = 0; + // Const that was #[doc(hidden)] will be removed + #[doc(hidden)] + pub const DocHiddenConstantA: i32 = 0; + // Should Be caught on renaming + pub const PublicConstantRenameA: i32 = 0; + // Should not be caught on removing since its not pub + const ConstantA: i32 = 0; +} + +struct StructB {} + +impl StructB { + // Should not be caught since StructB is not pub + pub const PublicConstantC: i32 = 0; + pub const PublicConstantRenameB: i32 = 0; + const ConstantB: i32 = 0; +} + +#[doc(hidden)] +pub struct StructC {} + +impl StructC { + // Should not be caught on removing or renaming since the struct #[doc(hidden)] + pub const PublicConstantD: i32 = 0; + pub const PublicConstantRenameC: i32 = 0; +} + +pub struct StructD {} + +// The constants in this block shouldn't be flagged as removed, +// since they are `#[doc(hidden)]` as a result of the block itself. +#[doc(hidden)] +impl StructD { + pub const PublicConstantE: i32 = 0; + pub const PublicConstantF: i32 = 0; +} + +// Test Cases where #[doc(hidden)] is added +pub struct StructE {} + +// This impl block will be #[doc(hidden)] +impl StructE { + pub const PublicConstantH: i32 = 0; + // This will be removed + pub const PublicConstantI: i32 = 0; +} + +// This struct will be #[doc(hidden)] +pub struct StructF {} + +impl StructF { + pub const PublicConstantJ: i32 = 0; + // This const will be removed + pub const PublicConstantK: i32 = 0; +} + +// Test cases where #[doc(hidden)] is removed +#[doc(hidden)] +pub struct DocHiddenStruct {} + +impl DocHiddenStruct { + // This const will be removed + #[doc(hidden)] + pub const DocHiddenConstantB: i32 = 0; + pub const PublicConstantL: i32 = 0; + // This const will be removed + pub const PublicConstantM: i32 = 0; +} + +#[doc(hidden)] +impl DocHiddenStruct { + // This const will be removed + pub const PublicConstantN: i32 = 0; + pub const PublicConstantO: i32 = 0; +} + +// Test for when an inherent const is implemented as trait's const +pub trait TraitA {} + +pub struct ExampleA; + +impl ExampleA { + pub const N_A: i64 = 0; +} + +impl TraitA for ExampleA {} + +// Test for when an inherent const is implemented as trait's const but given a value in impl +pub trait TraitB {} + +pub struct ExampleB; + +impl ExampleB { + pub const N_B: i64 = 0; +} + +impl TraitB for ExampleB {} diff --git a/test_outputs/inherent_associated_pub_const_missing.output.ron b/test_outputs/inherent_associated_pub_const_missing.output.ron new file mode 100644 index 00000000..93f27952 --- /dev/null +++ b/test_outputs/inherent_associated_pub_const_missing.output.ron @@ -0,0 +1,37 @@ +{ + "./test_crates/inherent_associated_pub_const_missing/": [ + { + "associated_constant": String("PublicConstantA"), + "name": String("StructA"), + "path": List([ + String("inherent_associated_pub_const_missing"), + String("StructA"), + ]), + "span_begin_line": Uint64(6), + "span_filename": String("src/lib.rs"), + "visibility_limit": String("public"), + }, + { + "associated_constant": String("PublicConstantRenameA"), + "name": String("StructA"), + "path": List([ + String("inherent_associated_pub_const_missing"), + String("StructA"), + ]), + "span_begin_line": Uint64(12), + "span_filename": String("src/lib.rs"), + "visibility_limit": String("public"), + }, + { + "associated_constant": String("PublicConstantI"), + "name": String("StructE"), + "path": List([ + String("inherent_associated_pub_const_missing"), + String("StructE"), + ]), + "span_begin_line": Uint64(52), + "span_filename": String("src/lib.rs"), + "visibility_limit": String("public"), + }, + ], +} diff --git a/test_outputs/struct_now_doc_hidden.output.ron b/test_outputs/struct_now_doc_hidden.output.ron index 3ce3b4fc..8b717472 100644 --- a/test_outputs/struct_now_doc_hidden.output.ron +++ b/test_outputs/struct_now_doc_hidden.output.ron @@ -1,4 +1,15 @@ { + "./test_crates/inherent_associated_pub_const_missing/": [ + { + "path": List([ + String("inherent_associated_pub_const_missing"), + String("StructF"), + ]), + "span_begin_line": Uint64(41), + "span_filename": String("src/lib.rs"), + "struct_name": String("StructF"), + }, + ], "./test_crates/struct_now_doc_hidden/": [ { "path": List([