cargo-semver-checks 0.48.0

Scan your Rust crate for semver violations.
Documentation
SemverQuery(
    id: "enum_repr_transparent_removed",
    human_readable_name: "enum repr(transparent) removed",
    description: "A enum is no longer repr(transparent).",
    reference: Some(r#"
repr(transparent) was removed from an enum. This can cause its memory layout to change,
breaking FFI use cases.

Unlike with structs, repr(transparent) is always part of the ABI for enums since
all variants inherit the visibility of their parent type. Note that
> [repr(transparent)] can only be used on single-variant enum with a single non-zero-sized field
> (there may be additional zero-sized fields). [...]
https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent

cargo-semver-checks currently can't check whether a field is zero-sized or not.

However, the most commonly-used kind of zero-sized field is core::marker::PhantomData,
and we can hardcode its detection and proper handling.

To avoid false-positives, this query is restricted to checking only enums that in the baseline:
- are repr(transparent);
- have exactly one variant
- which has at most one non-PhantomData field
"#),
    required_update: Major,
    lint_level: Deny,

    reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#repr-transparent-remove"),
    query: r#"
    {
        CrateDiff {
            baseline {
                item {
                    ... on Enum {
                        visibility_limit @filter(op: "=", value: ["$public"]) @output

                        attribute {
                            old_attr: raw_attribute @output
                            content {
                                base @filter(op: "=", value: ["$repr"])
                                argument {
                                    base @filter(op: "=", value: ["$transparent"])
                                }
                            }
                        }

                        # To avoid false-positives (as described above), we check two things:
                        # - this enum has one variant
                        # - this variant has a total of one field that isn't PhantomData

                        variant @fold @transform(op: "count") @filter(op: "=", value: ["$one"]) {
                            transparent_variant_name: name @output
                            field @fold @transform(op: "count") @filter(op: "=", value: ["$one"]) {
                                raw_type @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) {
                                    ... on ResolvedPathType {
                                        name @filter(op: "regex", value: ["$phantom_data"])
                                    }
                                }
                            }
                        }

                        importable_path {
                            path @tag @output
                            public_api @filter(op: "=", value: ["$true"])
                        }
                    }
                }
            }
            current {
                item {
                    ... on Enum {
                        visibility_limit @filter(op: "=", value: ["$public"])
                        name @output

                        attribute @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) {
                            content {
                                base @filter(op: "=", value: ["$repr"])
                                argument {
                                    base @filter(op: "=", value: ["$transparent"])
                                }
                            }
                        }

                        importable_path {
                            path @filter(op: "=", value: ["%path"])
                            public_api @filter(op: "=", value: ["$true"])
                        }

                        span_: span @optional {
                            filename @output
                            begin_line @output
                            end_line @output
                        }
                    }
                }
            }
        }
    }"#,
    arguments: {
        "public": "public",
        "repr": "repr",
        "transparent": "transparent",
        "phantom_data": "(?:core|std)::marker::PhantomData<",
        "true": true,
        "one": 1,
        "zero": 0,
    },
    error_message: "repr(transparent) was removed from an enum. This can cause its memory layout to change, breaking FFI use cases.",
    per_result_error_template: Some("enum {{name}} in {{span_filename}}:{{span_begin_line}}"),
)