diff --git a/src/lints/enum_variant_no_longer_non_exhaustive.ron b/src/lints/enum_variant_no_longer_non_exhaustive.ron new file mode 100644 index 00000000..4ffb5369 --- /dev/null +++ b/src/lints/enum_variant_no_longer_non_exhaustive.ron @@ -0,0 +1,63 @@ +SemverQuery( + id: "enum_variant_no_longer_non_exhaustive", + human_readable_name: "enum variant no longer #[non_exhaustive]", + description: "A pub enum variant is no longer #[non_exhaustive]. Future fields added to this variant will cause a breaking change.", + required_update: Minor, + lint_level: Allow, + reference_link: Some("https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute"), + query: r#" + { + CrateDiff { + baseline { + item { + ... on Enum { + visibility_limit @filter(op: "=", value: ["$public"]) @output + + importable_path { + path @output @tag + public_api @filter(op: "=", value: ["$true"]) + } + + variant { + variant_name: name @output @tag + public_api_eligible @filter(op: "=", value: ["$true"]) + attrs @filter(op: "contains", value: ["$non_exhaustive"]) + } + } + } + } + current { + item { + ... on Enum { + visibility_limit @filter(op: "=", value: ["$public"]) + enum_name: name @output + + importable_path { + path @filter(op: "=", value: ["%path"]) + public_api @filter(op: "=", value: ["$true"]) + } + + variant { + name @filter(op: "=", value: ["%variant_name"]) + public_api_eligible @filter(op: "=", value: ["$true"]) + attrs @filter(op: "not_contains", value: ["$non_exhaustive"]) + + span_: span @optional { + filename @output + begin_line @output + end_line @output + } + } + } + } + } + } + }"#, + arguments: { + "public": "public", + "non_exhaustive": "#[non_exhaustive]", + "true": true, + }, + error_message: "A pub enum variant is no longer #[non_exhaustive]. Future fields added to this variant will cause a breaking change.", + per_result_error_template: Some("variant {{enum_name}}:{{variant_name}} in {{span_filename}}:{{span_begin_line}}"), +) diff --git a/src/query.rs b/src/query.rs index 01c80137..b3b193aa 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1253,6 +1253,7 @@ add_lints!( enum_variant_marked_deprecated, enum_variant_marked_non_exhaustive, enum_variant_missing, + enum_variant_no_longer_non_exhaustive, exported_function_changed_abi, feature_missing, feature_not_enabled_by_default, diff --git a/test_crates/enum_variant_no_longer_non_exhaustive/new/Cargo.toml b/test_crates/enum_variant_no_longer_non_exhaustive/new/Cargo.toml new file mode 100644 index 00000000..518a2d47 --- /dev/null +++ b/test_crates/enum_variant_no_longer_non_exhaustive/new/Cargo.toml @@ -0,0 +1,7 @@ +[package] +publish = false +name = "enum_variant_no_longer_non_exhaustive" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test_crates/enum_variant_no_longer_non_exhaustive/new/src/lib.rs b/test_crates/enum_variant_no_longer_non_exhaustive/new/src/lib.rs new file mode 100644 index 00000000..ea1a6bf2 --- /dev/null +++ b/test_crates/enum_variant_no_longer_non_exhaustive/new/src/lib.rs @@ -0,0 +1,66 @@ +#![no_std] + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +pub enum ExhaustiveEnumUnitVariant { + UnitVariant, +} + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +#[non_exhaustive] +pub enum NonExhaustiveEnumUnitVariant { + UnitVariant, +} + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +pub enum ExhaustiveEnumTupleVariant { + TupleVariant(i32, i64), +} + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +#[non_exhaustive] +pub enum NonExhaustiveEnumTupleVariant { + TupleVariant(i32, i64), +} + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +pub enum ExhaustiveEnumStructVariant { + StructVariant { f: i32 }, +} + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +#[non_exhaustive] +pub enum NonExhaustiveEnumStructVariant { + StructVariant { f: i32 }, +} + +// This enum has a mix of variant types to test different variant kinds +pub enum MixedVariantKinds { + // Unit variant to be no longer #[non_exhaustive] + UnitVariant, + // Tuple variant to be no longer #[non_exhaustive] + TupleVariant(i32, i64), + // Struct variant to be no longer #[non_exhaustive] + StructVariant { field1: bool, field2: u32 }, +} + +// This enum's variant is already exhaustive and should not trigger the lint. +pub enum EnumWithAlreadyExhaustiveVariant { + ExhaustiveVariant, +} + +// This enum is hidden with #[doc(hidden)] and should not trigger the lint +#[doc(hidden)] +pub enum HiddenEnum { + Variant, +} + +// This enum has a hidden variant that should not trigger the lint +pub enum EnumWithHiddenVariant { + #[doc(hidden)] + HiddenVariantToBeExhaustive, +} + +// This enum is private and should not trigger the lint +enum PrivateEnum { + Variant, +} diff --git a/test_crates/enum_variant_no_longer_non_exhaustive/old/Cargo.toml b/test_crates/enum_variant_no_longer_non_exhaustive/old/Cargo.toml new file mode 100644 index 00000000..518a2d47 --- /dev/null +++ b/test_crates/enum_variant_no_longer_non_exhaustive/old/Cargo.toml @@ -0,0 +1,7 @@ +[package] +publish = false +name = "enum_variant_no_longer_non_exhaustive" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test_crates/enum_variant_no_longer_non_exhaustive/old/src/lib.rs b/test_crates/enum_variant_no_longer_non_exhaustive/old/src/lib.rs new file mode 100644 index 00000000..c9a53f39 --- /dev/null +++ b/test_crates/enum_variant_no_longer_non_exhaustive/old/src/lib.rs @@ -0,0 +1,79 @@ +#![no_std] + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +pub enum ExhaustiveEnumUnitVariant { + #[non_exhaustive] + UnitVariant, +} + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +#[non_exhaustive] +pub enum NonExhaustiveEnumUnitVariant { + #[non_exhaustive] + UnitVariant, +} + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +pub enum ExhaustiveEnumTupleVariant { + #[non_exhaustive] + TupleVariant(i32, i64), +} + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +#[non_exhaustive] +pub enum NonExhaustiveEnumTupleVariant { + #[non_exhaustive] + TupleVariant(i32, i64), +} + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +pub enum ExhaustiveEnumStructVariant { + #[non_exhaustive] + StructVariant { f: i32 }, +} + +// This pub enum's variant is no longer #[non_exhaustive], should trigger the lint. +#[non_exhaustive] +pub enum NonExhaustiveEnumStructVariant { + #[non_exhaustive] + StructVariant { f: i32 }, +} + +// This enum has a mix of variant types to test different variant kinds +pub enum MixedVariantKinds { + // Unit variant to be no longer #[non_exhaustive] + #[non_exhaustive] + UnitVariant, + // Tuple variant to be no longer #[non_exhaustive] + #[non_exhaustive] + TupleVariant(i32, i64), + // Struct variant to be no longer #[non_exhaustive] + #[non_exhaustive] + StructVariant { field1: bool, field2: u32 }, +} + +// This enum's variant is already exhaustive and should not trigger the lint. +pub enum EnumWithAlreadyExhaustiveVariant { + ExhaustiveVariant, +} + + +// This enum is hidden with #[doc(hidden)] and should not trigger the lint +#[doc(hidden)] +pub enum HiddenEnum { + #[non_exhaustive] + Variant, +} + +// This enum has a hidden variant that should not trigger the lint +pub enum EnumWithHiddenVariant { + #[non_exhaustive] + #[doc(hidden)] + HiddenVariantToBeExhaustive, +} + +// This enum is private and should not trigger the lint +enum PrivateEnum { + #[non_exhaustive] + Variant, +} diff --git a/test_outputs/query_execution/enum_variant_no_longer_non_exhaustive.snap b/test_outputs/query_execution/enum_variant_no_longer_non_exhaustive.snap new file mode 100644 index 00000000..e32032f3 --- /dev/null +++ b/test_outputs/query_execution/enum_variant_no_longer_non_exhaustive.snap @@ -0,0 +1,144 @@ +--- +source: src/query.rs +expression: "&query_execution_results" +--- +{ + "./test_crates/enum_no_repr_variant_discriminant_changed/": [ + { + "enum_name": String("DiscriminantIsChangedAndVariantNoLongerNonExhaustive"), + "path": List([ + String("enum_no_repr_variant_discriminant_changed"), + String("DiscriminantIsChangedAndVariantNoLongerNonExhaustive"), + ]), + "span_begin_line": Uint64(103), + "span_end_line": Uint64(103), + "span_filename": String("src/lib.rs"), + "variant_name": String("First"), + "visibility_limit": String("public"), + }, + ], + "./test_crates/enum_tuple_variant_field_added/": [ + { + "enum_name": String("PublicEnum"), + "path": List([ + String("enum_tuple_variant_field_added"), + String("PublicEnum"), + ]), + "span_begin_line": Uint64(10), + "span_end_line": Uint64(10), + "span_filename": String("src/lib.rs"), + "variant_name": String("TupleVariantWithFieldAddedBecomesExhaustive"), + "visibility_limit": String("public"), + }, + ], + "./test_crates/enum_variant_no_longer_non_exhaustive/": [ + { + "enum_name": String("ExhaustiveEnumUnitVariant"), + "path": List([ + String("enum_variant_no_longer_non_exhaustive"), + String("ExhaustiveEnumUnitVariant"), + ]), + "span_begin_line": Uint64(5), + "span_end_line": Uint64(5), + "span_filename": String("src/lib.rs"), + "variant_name": String("UnitVariant"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("NonExhaustiveEnumUnitVariant"), + "path": List([ + String("enum_variant_no_longer_non_exhaustive"), + String("NonExhaustiveEnumUnitVariant"), + ]), + "span_begin_line": Uint64(11), + "span_end_line": Uint64(11), + "span_filename": String("src/lib.rs"), + "variant_name": String("UnitVariant"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("ExhaustiveEnumTupleVariant"), + "path": List([ + String("enum_variant_no_longer_non_exhaustive"), + String("ExhaustiveEnumTupleVariant"), + ]), + "span_begin_line": Uint64(16), + "span_end_line": Uint64(16), + "span_filename": String("src/lib.rs"), + "variant_name": String("TupleVariant"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("NonExhaustiveEnumTupleVariant"), + "path": List([ + String("enum_variant_no_longer_non_exhaustive"), + String("NonExhaustiveEnumTupleVariant"), + ]), + "span_begin_line": Uint64(22), + "span_end_line": Uint64(22), + "span_filename": String("src/lib.rs"), + "variant_name": String("TupleVariant"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("ExhaustiveEnumStructVariant"), + "path": List([ + String("enum_variant_no_longer_non_exhaustive"), + String("ExhaustiveEnumStructVariant"), + ]), + "span_begin_line": Uint64(27), + "span_end_line": Uint64(27), + "span_filename": String("src/lib.rs"), + "variant_name": String("StructVariant"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("NonExhaustiveEnumStructVariant"), + "path": List([ + String("enum_variant_no_longer_non_exhaustive"), + String("NonExhaustiveEnumStructVariant"), + ]), + "span_begin_line": Uint64(33), + "span_end_line": Uint64(33), + "span_filename": String("src/lib.rs"), + "variant_name": String("StructVariant"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("MixedVariantKinds"), + "path": List([ + String("enum_variant_no_longer_non_exhaustive"), + String("MixedVariantKinds"), + ]), + "span_begin_line": Uint64(39), + "span_end_line": Uint64(39), + "span_filename": String("src/lib.rs"), + "variant_name": String("UnitVariant"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("MixedVariantKinds"), + "path": List([ + String("enum_variant_no_longer_non_exhaustive"), + String("MixedVariantKinds"), + ]), + "span_begin_line": Uint64(41), + "span_end_line": Uint64(41), + "span_filename": String("src/lib.rs"), + "variant_name": String("TupleVariant"), + "visibility_limit": String("public"), + }, + { + "enum_name": String("MixedVariantKinds"), + "path": List([ + String("enum_variant_no_longer_non_exhaustive"), + String("MixedVariantKinds"), + ]), + "span_begin_line": Uint64(43), + "span_end_line": Uint64(43), + "span_filename": String("src/lib.rs"), + "variant_name": String("StructVariant"), + "visibility_limit": String("public"), + }, + ], +}