diff --git a/scripts/regenerate_test_rustdocs.sh b/scripts/regenerate_test_rustdocs.sh index e6ea43a9..7e0b669f 100755 --- a/scripts/regenerate_test_rustdocs.sh +++ b/scripts/regenerate_test_rustdocs.sh @@ -24,6 +24,7 @@ features=( 'struct_pub_field_missing' 'enum_missing' 'enum_variant_missing' + 'unit_struct_changed_kind' ) for feat in "${features[@]}" do diff --git a/semver_tests/Cargo.toml b/semver_tests/Cargo.toml index 5afd10e7..763aeffb 100644 --- a/semver_tests/Cargo.toml +++ b/semver_tests/Cargo.toml @@ -13,3 +13,4 @@ struct_missing = [] struct_pub_field_missing = [] enum_missing = [] enum_variant_missing = [] +unit_struct_changed_kind = [] diff --git a/semver_tests/src/test_cases/mod.rs b/semver_tests/src/test_cases/mod.rs index 16b30ed2..0c0b835c 100644 --- a/semver_tests/src/test_cases/mod.rs +++ b/semver_tests/src/test_cases/mod.rs @@ -23,3 +23,17 @@ pub enum VariantWillBeRemoved { #[cfg(not(feature = "enum_variant_missing"))] Bar, } + +#[cfg(not(feature = "unit_struct_changed_kind"))] +pub struct UnitStructToPlain; + +#[cfg(feature = "unit_struct_changed_kind")] +pub struct UnitStructToPlain {} + +#[cfg(not(feature = "unit_struct_changed_kind"))] +#[non_exhaustive] +pub struct NonExhaustiveUnitStructToPlain; + +#[cfg(feature = "unit_struct_changed_kind")] +#[non_exhaustive] +pub struct NonExhaustiveUnitStructToPlain {} diff --git a/src/adapter.rs b/src/adapter.rs index b4c6e74d..42ec6df0 100644 --- a/src/adapter.rs +++ b/src/adapter.rs @@ -748,6 +748,7 @@ mod tests { enum_variant_missing, struct_missing, struct_pub_field_missing, + unit_struct_changed_kind, struct_marked_non_exhaustive, ); } diff --git a/src/queries/unit_struct_changed_kind.ron b/src/queries/unit_struct_changed_kind.ron new file mode 100644 index 00000000..94171cd4 --- /dev/null +++ b/src/queries/unit_struct_changed_kind.ron @@ -0,0 +1,59 @@ +SemverQuery( + id: "unit_struct_changed_kind", + human_readable_name: "unit struct changed kind", + description: "A public struct that was previously a unit struct is now a plain struct. The unit struct was not marked #[non_exhaustive], so it could be constructed outside of the defining crate. Plain structs cannot be constructed using the syntax allowed for unit structs, so this is a major breaking change for code that depends on it.", + required_update: Major, + + // TODO: Change the reference link once this cargo docs PR merges: + // https://github.com/rust-lang/cargo/pull/10871 + // + // Change to this link: + // https://doc.rust-lang.org/cargo/reference/semver.html#struct-unit-to-normal + reference_link: Some("https://github.com/rust-lang/cargo/pull/10871"), + query: r#" + { + CrateDiff { + baseline { + item { + ... on Struct { + visibility_limit @filter(op: "=", value: ["$public"]) @output + name @output @tag + struct_type @filter(op: "=", value: ["$unit"]) + attrs @filter(op: "not_contains", value: ["$non_exhaustive"]) + + path { + path @output @tag + } + + span_: span @optional { + filename @output + begin_line @output + } + } + } + } + current @fold @transform(op: "count") @filter(op: ">=", value: ["$one"]) { + item { + ... on Struct { + visibility_limit @filter(op: "=", value: ["$public"]) + name @filter(op: "=", value: ["%name"]) + struct_type @filter(op: "=", value: ["$plain"]) + + path { + path @filter(op: "=", value: ["%path"]) + } + } + } + } + } + }"#, + arguments: { + "public": "public", + "unit": "unit", + "plain": "plain", + "one": 1, + "non_exhaustive": "#[non_exhaustive]", + }, + error_message: "A public unit struct has been changed to a normal (curly-braces) struct, which cannot be constructed using the same struct literal syntax.", + per_result_error_template: Some("struct {{name}}, previously in file {{span_filename}}:{{span_begin_line}}"), +) diff --git a/src/test_data/unit_struct_changed_kind.output.ron b/src/test_data/unit_struct_changed_kind.output.ron new file mode 100644 index 00000000..306d628d --- /dev/null +++ b/src/test_data/unit_struct_changed_kind.output.ron @@ -0,0 +1,9 @@ +[ + { + "name": String("UnitStructToPlain"), + "path": List([String("semver_tests"), String("test_cases"), String("UnitStructToPlain")]), + "visibility_limit": String("public"), + "span_filename": String("src/test_cases/mod.rs"), + "span_begin_line": Uint64(28), + } +]