From 0e24e8e117e3aa54f18ac5004ba5bb41ec2ed465 Mon Sep 17 00:00:00 2001 From: David Matos Date: Tue, 3 Sep 2024 17:28:24 +0200 Subject: [PATCH] Add enum variant discriminant changed lint --- .../enum_variant_discriminant_changed.ron | 65 +++++++++++++++++++ src/query.rs | 1 + .../new/Cargo.toml | 7 ++ .../new/src/lib.rs | 31 +++++++++ .../old/Cargo.toml | 7 ++ .../old/src/lib.rs | 30 +++++++++ ...um_variant_discriminant_changed.output.ron | 56 ++++++++++++++++ 7 files changed, 197 insertions(+) create mode 100644 src/lints/enum_variant_discriminant_changed.ron create mode 100644 test_crates/enum_variant_discriminant_changed/new/Cargo.toml create mode 100644 test_crates/enum_variant_discriminant_changed/new/src/lib.rs create mode 100644 test_crates/enum_variant_discriminant_changed/old/Cargo.toml create mode 100644 test_crates/enum_variant_discriminant_changed/old/src/lib.rs create mode 100644 test_outputs/enum_variant_discriminant_changed.output.ron diff --git a/src/lints/enum_variant_discriminant_changed.ron b/src/lints/enum_variant_discriminant_changed.ron new file mode 100644 index 00000000..b33b33f0 --- /dev/null +++ b/src/lints/enum_variant_discriminant_changed.ron @@ -0,0 +1,65 @@ +SemverQuery( + id: "enum_variant_discriminant_changed", + human_readable_name: "Public enum's variant had its discriminant changed from its previous value", + description: "A public enum's variant had its discriminant changed from its previous value.", + reference: Some("The public enum's variant had its discriminant changed from its previous value. This can cause compatibility issues, which may break FFI use cases."), + required_update: Major, + lint_level: Deny, + reference_link: Some("https://doc.rust-lang.org/reference/items/enumerations.html#discriminants"), + query: r#" + { + CrateDiff { + baseline { + item { + ... on Enum { + visibility_limit @filter(op: "=", value: ["$public"]) @output + enum_name: name @output @tag + + importable_path { + path @output @tag + public_api @filter(op: "=", value: ["$true"]) + } + + variant { + variant_name: name @output @tag + discriminant @optional { + old_value: value @output @tag + } + } + } + } + } + current { + item { + ... on Enum { + visibility_limit @filter(op: "=", value: ["$public"]) + name @filter(op: "=", value: ["%enum_name"]) + + importable_path { + path @filter(op: "=", value: ["%path"]) + public_api @filter(op: "=", value: ["$true"]) + } + + variant { + name @filter(op: "=", value: ["%variant_name"]) + discriminant { + new_value: value @output @filter (op: "!=", value: ["%old_value"]) + } + } + + span_: span @optional { + filename @output + begin_line @output + } + } + } + } + } + }"#, + arguments: { + "public": "public", + "true": true, + }, + error_message: "The public enum's variant had its discriminant changed from its previous value. This can cause compatibility issues, which may break FFI use cases.", + per_result_error_template: Some("variant {{enum_name}}::{{variant_name}} {{old_value}} -> {{new_value}} in {{span_filename}}:{{span_begin_line}}"), +) diff --git a/src/query.rs b/src/query.rs index be1696e1..520cdd83 100644 --- a/src/query.rs +++ b/src/query.rs @@ -805,6 +805,7 @@ add_lints!( enum_tuple_variant_field_now_doc_hidden, enum_unit_variant_changed_kind, enum_variant_added, + enum_variant_discriminant_changed, enum_variant_marked_non_exhaustive, enum_variant_missing, exported_function_changed_abi, diff --git a/test_crates/enum_variant_discriminant_changed/new/Cargo.toml b/test_crates/enum_variant_discriminant_changed/new/Cargo.toml new file mode 100644 index 00000000..a36927a1 --- /dev/null +++ b/test_crates/enum_variant_discriminant_changed/new/Cargo.toml @@ -0,0 +1,7 @@ +[package] +publish = false +name = "enum_variant_discriminant_changed" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test_crates/enum_variant_discriminant_changed/new/src/lib.rs b/test_crates/enum_variant_discriminant_changed/new/src/lib.rs new file mode 100644 index 00000000..be64ea6b --- /dev/null +++ b/test_crates/enum_variant_discriminant_changed/new/src/lib.rs @@ -0,0 +1,31 @@ +// Explicit discriminant changed values. By doing so, it changed the implicit +// discriminant's value as well, should be reported. +#[repr(u8, C)] +pub enum ExplicitAndImplicitDiscriminantsAreChanged { + First = 2, + Second, + Third = 5, +} + +// Implicit discriminant changed values when becoming explicit, should be reported. +#[repr(u8)] +pub enum ImplicitDiscriminantBecomesExplicit { + Tuple(), + Struct, + Unit = 5, +} + +// Discriminant changed to be doc hidden and explicit. Being doc hidden is not relevant +// since it's still part of the public API, should be reported. +pub enum DiscriminantBecomesDocHiddenAndExplicit { + First, + #[doc(hidden)] + Second = 2, +} + +// Explicit discriminants changed values, but being private dominates, should not be +// reported. +enum PrivateEnum { + First = 10, + Second = 11, +} diff --git a/test_crates/enum_variant_discriminant_changed/old/Cargo.toml b/test_crates/enum_variant_discriminant_changed/old/Cargo.toml new file mode 100644 index 00000000..a36927a1 --- /dev/null +++ b/test_crates/enum_variant_discriminant_changed/old/Cargo.toml @@ -0,0 +1,7 @@ +[package] +publish = false +name = "enum_variant_discriminant_changed" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test_crates/enum_variant_discriminant_changed/old/src/lib.rs b/test_crates/enum_variant_discriminant_changed/old/src/lib.rs new file mode 100644 index 00000000..c1b1db4e --- /dev/null +++ b/test_crates/enum_variant_discriminant_changed/old/src/lib.rs @@ -0,0 +1,30 @@ +// Explicit discriminant changed values. By doing so, it changed the implicit +// discriminant's value as well, should be reported. +#[repr(u8, C)] +pub enum ExplicitAndImplicitDiscriminantsAreChanged { + First = 1, + Second, + Third = 5, +} + +// Implicit discriminant changed values when becoming explicit, should be reported. +#[repr(u8)] +pub enum ImplicitDiscriminantBecomesExplicit { + Tuple(), + Struct {}, + Unit, +} + +// Discriminant changed to be doc hidden and explicit. Being doc hidden is not relevant +// since it's still part of the public API, should be reported. +pub enum DiscriminantBecomesDocHiddenAndExplicit { + First, + Second, +} + +// Explicit discriminants changed values, but being private dominates, should not be +// reported. +enum PrivateEnum { + First = 1, + Second = 2, +} diff --git a/test_outputs/enum_variant_discriminant_changed.output.ron b/test_outputs/enum_variant_discriminant_changed.output.ron new file mode 100644 index 00000000..0b9b3015 --- /dev/null +++ b/test_outputs/enum_variant_discriminant_changed.output.ron @@ -0,0 +1,56 @@ +{ + "./test_crates/enum_variant_discriminant_changed/": [ + { + "enum_name": String("ExplicitAndImplicitDiscriminantsAreChanged"), + "new_value": String("2"), + "old_value": String("1"), + "path": List([ + String("enum_variant_discriminant_changed"), + String("ExplicitAndImplicitDiscriminantsAreChanged"), + ]), + "span_begin_line": Uint64(4), + "span_filename": String("src/lib.rs"), + "variant_name": String("First"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("ExplicitAndImplicitDiscriminantsAreChanged"), + "new_value": String("3"), + "old_value": String("2"), + "path": List([ + String("enum_variant_discriminant_changed"), + String("ExplicitAndImplicitDiscriminantsAreChanged"), + ]), + "span_begin_line": Uint64(4), + "span_filename": String("src/lib.rs"), + "variant_name": String("Second"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("ImplicitDiscriminantBecomesExplicit"), + "new_value": String("5"), + "old_value": String("2"), + "path": List([ + String("enum_variant_discriminant_changed"), + String("ImplicitDiscriminantBecomesExplicit"), + ]), + "span_begin_line": Uint64(12), + "span_filename": String("src/lib.rs"), + "variant_name": String("Unit"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("DiscriminantBecomesDocHiddenAndExplicit"), + "new_value": String("2"), + "old_value": String("1"), + "path": List([ + String("enum_variant_discriminant_changed"), + String("DiscriminantBecomesDocHiddenAndExplicit"), + ]), + "span_begin_line": Uint64(20), + "span_filename": String("src/lib.rs"), + "variant_name": String("Second"), + "visibility_limit": String("public"), + }, + ] +}