diff --git a/scripts/regenerate_test_rustdocs.sh b/scripts/regenerate_test_rustdocs.sh index 866aeed3..4ecf524f 100755 --- a/scripts/regenerate_test_rustdocs.sh +++ b/scripts/regenerate_test_rustdocs.sh @@ -22,6 +22,8 @@ mv "$RUSTDOC_OUTPUT" "$TARGET_DIR/baseline.json" features=( 'enum_missing' 'enum_repr_c_removed' + 'enum_repr_int_changed' + 'enum_repr_int_removed' 'enum_variant_added' 'enum_variant_missing' 'function_missing' diff --git a/semver_tests/Cargo.toml b/semver_tests/Cargo.toml index 10ff9e09..1d3225c6 100644 --- a/semver_tests/Cargo.toml +++ b/semver_tests/Cargo.toml @@ -9,6 +9,8 @@ edition = "2021" [features] enum_missing = [] +enum_repr_int_changed = [] +enum_repr_int_removed = [] enum_repr_c_removed = [] enum_variant_added = [] enum_variant_missing = [] diff --git a/semver_tests/src/test_cases/enum_repr_int_changed.rs b/semver_tests/src/test_cases/enum_repr_int_changed.rs new file mode 100644 index 00000000..9bc8d9dc --- /dev/null +++ b/semver_tests/src/test_cases/enum_repr_int_changed.rs @@ -0,0 +1,71 @@ +#[cfg(not(feature = "enum_repr_int_changed"))] +#[repr(u8)] +pub enum U8ToU16Enum { + Bar, + Baz, +} + +#[cfg(feature = "enum_repr_int_changed")] +#[repr(u16)] +pub enum U8ToU16Enum { + Bar, + Baz, +} + +#[cfg(not(feature = "enum_repr_int_changed"))] +#[repr(i32)] +pub enum I32ToI8Enum { + Bar, + Baz, +} + +#[cfg(feature = "enum_repr_int_changed")] +#[repr(i8)] +pub enum I32ToI8Enum { + Bar, + Baz, +} + +#[cfg(not(feature = "enum_repr_int_changed"))] +#[repr(i32)] +pub enum I32ToU32Enum { + Bar, + Baz, +} + +#[cfg(feature = "enum_repr_int_changed")] +#[repr(u32)] +pub enum I32ToU32Enum { + Bar, + Baz, +} + +// The following enums have *removals* of repr(i*) and repr(u*), +// not changes to another repr(i*) or repr(u*). +// They should not be reported by this rule, because they have their own rule. + +#[cfg(not(feature = "enum_repr_int_changed"))] +#[repr(u8)] +pub enum U8Enum { + Bar, + Baz, +} + +#[cfg(feature = "enum_repr_int_changed")] +pub enum U8Enum { + Bar, + Baz, +} + +#[cfg(not(feature = "enum_repr_int_changed"))] +#[repr(i32)] +pub enum I32Enum { + Bar, + Baz, +} + +#[cfg(feature = "enum_repr_int_changed")] +pub enum I32Enum { + Bar, + Baz, +} diff --git a/semver_tests/src/test_cases/enum_repr_int_removed.rs b/semver_tests/src/test_cases/enum_repr_int_removed.rs new file mode 100644 index 00000000..6cb0585d --- /dev/null +++ b/semver_tests/src/test_cases/enum_repr_int_removed.rs @@ -0,0 +1,25 @@ +#[cfg(not(feature = "enum_repr_int_removed"))] +#[repr(u8)] +pub enum U8Enum { + Bar, + Baz, +} + +#[cfg(feature = "enum_repr_int_removed")] +pub enum U8Enum { + Bar, + Baz, +} + +#[cfg(not(feature = "enum_repr_int_removed"))] +#[repr(i32)] +pub enum I32Enum { + Bar, + Baz, +} + +#[cfg(feature = "enum_repr_int_removed")] +pub enum I32Enum { + Bar, + Baz, +} diff --git a/semver_tests/src/test_cases/mod.rs b/semver_tests/src/test_cases/mod.rs index 806784b1..6948e435 100644 --- a/semver_tests/src/test_cases/mod.rs +++ b/semver_tests/src/test_cases/mod.rs @@ -1,4 +1,6 @@ pub mod enum_repr_c_removed; +pub mod enum_repr_int_changed; +pub mod enum_repr_int_removed; pub mod enum_variant_added; pub mod enum_variant_missing; pub mod item_missing; diff --git a/src/adapter.rs b/src/adapter.rs index e89fcdb7..bfa55246 100644 --- a/src/adapter.rs +++ b/src/adapter.rs @@ -61,6 +61,13 @@ impl Origin { kind: TokenKind::RawType(raw_type), } } + + fn make_attribute_token<'a>(&self, attr: &'a str) -> Token<'a> { + Token { + origin: *self, + kind: TokenKind::Attribute(attr), + } + } } #[derive(Debug, Clone)] @@ -69,15 +76,6 @@ pub struct Token<'a> { kind: TokenKind<'a>, } -impl<'a> Token<'a> { - fn new_crate(origin: Origin, crate_: &'a Crate) -> Self { - Self { - origin, - kind: TokenKind::Crate(crate_), - } - } -} - #[derive(Debug, Clone)] pub enum TokenKind<'a> { CrateDiff((&'a Crate, &'a Crate)), @@ -86,10 +84,18 @@ pub enum TokenKind<'a> { Span(&'a Span), Path(&'a [String]), RawType(&'a Type), + Attribute(&'a str), } #[allow(dead_code)] impl<'a> Token<'a> { + fn new_crate(origin: Origin, crate_: &'a Crate) -> Self { + Self { + origin, + kind: TokenKind::Crate(crate_), + } + } + /// The name of the actual runtime type of this token, /// intended to fulfill resolution requests for the __typename property. #[inline] @@ -111,6 +117,7 @@ impl<'a> Token<'a> { TokenKind::Path(..) => "Path", TokenKind::Crate(..) => "Crate", TokenKind::CrateDiff(..) => "CrateDiff", + TokenKind::Attribute(..) => "Attribute", TokenKind::RawType(ty) => match ty { rustdoc_types::Type::ResolvedPath { .. } => "ResolvedPathType", rustdoc_types::Type::Primitive(..) => "PrimitiveType", @@ -203,6 +210,13 @@ impl<'a> Token<'a> { }) } + fn as_attribute(&self) -> Option<&'a str> { + match &self.kind { + TokenKind::Attribute(attr) => Some(*attr), + _ => None, + } + } + fn as_raw_type(&self) -> Option<&'a rustdoc_types::Type> { match &self.kind { TokenKind::RawType(ty) => Some(*ty), @@ -337,6 +351,14 @@ fn get_impl_property(token: &Token, field_name: &str) -> FieldValue { } } +fn get_attribute_property(token: &Token, field_name: &str) -> FieldValue { + let attribute_token = token.as_attribute().expect("token was not an Attribute"); + match field_name { + "value" => attribute_token.into(), + _ => unreachable!("Attribute property {field_name}"), + } +} + fn get_resolved_path_type_property(token: &Token, field_name: &str) -> FieldValue { let type_token = token.as_raw_type().expect("token was not a RawType"); match type_token { @@ -467,6 +489,9 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> { property_mapper(ctx, field_name.as_ref(), get_impl_property) })) } + "Attribute" => Box::new(data_contexts.map(move |ctx| { + property_mapper(ctx, field_name.as_ref(), get_attribute_property) + })), "ResolvedPathType" => Box::new(data_contexts.map(move |ctx| { property_mapper(ctx, field_name.as_ref(), get_resolved_path_type_property) })), @@ -607,25 +632,47 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> { "Item" | "ImplOwner" | "Struct" | "StructField" | "Enum" | "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" | "Function" | "Method" | "Impl" - if edge_name.as_ref() == "span" => + if matches!(edge_name.as_ref(), "span" | "attribute") => { - Box::new(data_contexts.map(move |ctx| { - let neighbors: Box + 'a> = - match &ctx.current_token { - None => Box::new(std::iter::empty()), - Some(token) => { - let origin = token.origin; - let item = token.as_item().expect("token was not an Item"); - if let Some(span) = &item.span { - Box::new(std::iter::once(origin.make_span_token(span))) - } else { - Box::new(std::iter::empty()) + match edge_name.as_ref() { + "span" => Box::new(data_contexts.map(move |ctx| { + let neighbors: Box + 'a> = + match &ctx.current_token { + None => Box::new(std::iter::empty()), + Some(token) => { + let origin = token.origin; + let item = token.as_item().expect("token was not an Item"); + if let Some(span) = &item.span { + Box::new(std::iter::once(origin.make_span_token(span))) + } else { + Box::new(std::iter::empty()) + } } - } - }; + }; - (ctx, neighbors) - })) + (ctx, neighbors) + })), + "attribute" => Box::new(data_contexts.map(move |ctx| { + let neighbors: Box + 'a> = + match &ctx.current_token { + None => Box::new(std::iter::empty()), + Some(token) => { + let origin = token.origin; + let item = token.as_item().expect("token was not an Item"); + Box::new( + item.attrs + .iter() + .map(move |attr| origin.make_attribute_token(attr)), + ) + } + }; + + (ctx, neighbors) + })), + _ => unreachable!( + "project_neighbors {current_type_name} {edge_name} {parameters:?}" + ), + } } "ImplOwner" | "Struct" | "Enum" if matches!(edge_name.as_ref(), "impl" | "inherent_impl") => @@ -959,6 +1006,8 @@ mod tests { query_execution_tests!( enum_missing, enum_repr_c_removed, + enum_repr_int_changed, + enum_repr_int_removed, enum_variant_added, enum_variant_missing, function_missing, diff --git a/src/queries/enum_repr_int_changed.ron b/src/queries/enum_repr_int_changed.ron new file mode 100644 index 00000000..a940002e --- /dev/null +++ b/src/queries/enum_repr_int_changed.ron @@ -0,0 +1,66 @@ +SemverQuery( + id: "enum_repr_int_changed", + human_readable_name: "enum repr(u*)/repr(i*) changed", + description: "The repr(u*) or repr(i*) attribute on an enum was changed to another integer type. This can cause its memory representation to change, breaking FFI use cases.", + required_update: Major, + + // TODO: Change the reference link to point to the cargo semver reference + // once it has a section on repr(u*)/repr(i*). + reference_link: Some("https://doc.rust-lang.org/nomicon/other-reprs.html#repru-repri"), + query: r#" + { + CrateDiff { + baseline { + item { + ... on Enum { + visibility_limit @filter(op: "=", value: ["$public"]) @output + name @tag @output + + attribute { + attr_value: value @filter(op: "regex", value: ["$repr_regex"]) + @tag @output(name: "old_attr") + } + + path { + path @tag @output + } + } + } + } + current { + item { + ... on Enum { + visibility_limit @filter(op: "=", value: ["$public"]) + name @filter(op: "=", value: ["%name"]) + + path { + path @filter(op: "=", value: ["%path"]) + } + + # Check that there exists an attribute that: + # - looks like repr(i*) or repr(u*) + # - but is not the same repr(i*) or repr(u*) that we had before. + # This is the breaking change. + attribute @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { + value @filter(op: "regex", value: ["$repr_regex"]) + @filter(op: "!=", value: ["%attr_value"]) + @output(name: "new_attr") + } + + span_: span @optional { + filename @output + begin_line @output + } + } + } + } + } + }"#, + arguments: { + "public": "public", + "repr_regex": "#\\[repr\\([ui]\\d+\\)\\]", + "zero": 0, + }, + error_message: "The repr(u*) or repr(i*) attribute on an enum was changed to another integer type. This can cause its memory representation to change, breaking FFI use cases.", + per_result_error_template: Some("enum {{name}} {{old_attr}} -> {{new_attr.0}} in {{span_filename}}:{{span_begin_line}}"), +) diff --git a/src/queries/enum_repr_int_removed.ron b/src/queries/enum_repr_int_removed.ron new file mode 100644 index 00000000..5a3c658f --- /dev/null +++ b/src/queries/enum_repr_int_removed.ron @@ -0,0 +1,59 @@ +SemverQuery( + id: "enum_repr_int_removed", + human_readable_name: "enum repr(u*)/repr(i*) removed", + description: "The repr(u*) or repr(i*) attribute was removed from an enum. This can cause its memory representation to change, breaking FFI use cases.", + required_update: Major, + + // TODO: Change the reference link to point to the cargo semver reference + // once it has a section on repr(u*)/repr(i*). + reference_link: Some("https://doc.rust-lang.org/nomicon/other-reprs.html#repru-repri"), + query: r#" + { + CrateDiff { + baseline { + item { + ... on Enum { + visibility_limit @filter(op: "=", value: ["$public"]) @output + name @tag @output + + attribute { + value @filter(op: "regex", value: ["$repr_regex"]) + } + + path { + path @tag @output + } + } + } + } + current { + item { + ... on Enum { + visibility_limit @filter(op: "=", value: ["$public"]) + name @filter(op: "=", value: ["%name"]) + + path { + path @filter(op: "=", value: ["%path"]) + } + + attribute @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { + value @filter(op: "regex", value: ["$repr_regex"]) + } + + span_: span @optional { + filename @output + begin_line @output + } + } + } + } + } + }"#, + arguments: { + "public": "public", + "repr_regex": "#\\[repr\\([ui]\\d+\\)\\]", + "zero": 0, + }, + error_message: "repr(u*) or repr(i*) was removed from an enum. This can cause its memory representation to change, breaking FFI use cases.", + per_result_error_template: Some("enum {{name}} in {{span_filename}}:{{span_begin_line}}"), +) diff --git a/src/query.rs b/src/query.rs index 5b3e72c3..90c9cbd9 100644 --- a/src/query.rs +++ b/src/query.rs @@ -66,6 +66,8 @@ impl SemverQuery { let query_text_contents = [ include_str!("./queries/enum_missing.ron"), include_str!("./queries/enum_repr_c_removed.ron"), + include_str!("./queries/enum_repr_int_changed.ron"), + include_str!("./queries/enum_repr_int_removed.ron"), include_str!("./queries/enum_variant_added.ron"), include_str!("./queries/enum_variant_missing.ron"), include_str!("./queries/function_missing.ron"), diff --git a/src/rustdoc_schema.graphql b/src/rustdoc_schema.graphql index 223d4d8b..164ae0de 100644 --- a/src/rustdoc_schema.graphql +++ b/src/rustdoc_schema.graphql @@ -1,5 +1,5 @@ schema { - query: RootSchemaQuery + query: RootSchemaQuery } directive @filter(op: String!, value: [String!]) on FIELD | INLINE_FRAGMENT directive @tag(name: String) on FIELD @@ -9,25 +9,25 @@ directive @recurse(depth: Int!) on FIELD directive @fold on FIELD type RootSchemaQuery { - Crate: Crate! - CrateDiff: CrateDiff! + Crate: Crate! + CrateDiff: CrateDiff! } type CrateDiff { - current: Crate! - baseline: Crate + current: Crate! + baseline: Crate } """ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Crate.html """ type Crate { - root: String! - crate_version: String - includes_private: Boolean! - format_version: Int! + root: String! + crate_version: String + includes_private: Boolean! + format_version: Int! - item: [Item!] + item: [Item!] } """ @@ -35,16 +35,24 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html """ interface Item { - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! + id: String! + crate_id: Int! + name: String + docs: String - # stringified version of the visibility struct field - visibility_limit: String! + """ + A list of all the attributes applied to this item. + + The attributes are also available through the `attribute` edge, + which makes certain operations easier. + """ + attrs: [String!]! - span: Span + # stringified version of the visibility struct field + visibility_limit: String! + + attribute: [Attribute!]! + span: Span } """ @@ -53,52 +61,52 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Struct.html """ type Struct implements Item & Importable & ImplOwner { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # own properties - struct_type: String! - fields_stripped: Boolean! - - # edges from Item - span: Span - - # edges from Importable - path: [Path!] - - # edges from ImplOwner - """ - Any impl for this type. - - All impl kinds are included: - - inherent impls: `impl Foo` - - explicit trait implementations: `impl Bar for Foo` - - blanket implementations: `impl Bar for T` - """ - impl: [Impl!] - - """ - Only inherent impls: implementations of the type itself (`impl Foo`). - - The impls pointed to here are guaranteed to have no `trait` and no `blanket` edges. - - This edge is just a convenience to simplify query-writing, - so we don't have to keep writing "@fold @transform(...) @filter(...)" chains - over the `trait` and `blanket` edges. - - When Trustfall supports macro edges, this should just become a macro edge. - """ - inherent_impl: [Impl!] - - # own edges - field: [StructField!] -} + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! + visibility_limit: String! + + # own properties + struct_type: String! + fields_stripped: Boolean! + + # edges from Item + span: Span + attribute: [Attribute!]! + + # edges from Importable + path: [Path!] + + # edges from ImplOwner + """ + Any impl for this type. + + All impl kinds are included: + - inherent impls: `impl Foo` + - explicit trait implementations: `impl Bar for Foo` + - blanket implementations: `impl Bar for T` + """ + impl: [Impl!] + + """ + Only inherent impls: implementations of the type itself (`impl Foo`). + The impls pointed to here are guaranteed to have no `trait` and no `blanket` edges. + + This edge is just a convenience to simplify query-writing, + so we don't have to keep writing "@fold @transform(...) @filter(...)" chains + over the `trait` and `blanket` edges. + + When Trustfall supports macro edges, this should just become a macro edge. + """ + inherent_impl: [Impl!] + + # own edges + field: [StructField!] +} """ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Item.html @@ -106,19 +114,20 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.Type.html """ type StructField implements Item { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span - - # own edges - raw_type: RawType + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! + visibility_limit: String! + + # edges from Item + span: Span + attribute: [Attribute!]! + + # own edges + raw_type: RawType } """ @@ -127,49 +136,50 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Enum.html """ type Enum implements Item & Importable & ImplOwner { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! + visibility_limit: String! - # own properties - variants_stripped: Boolean! + # own properties + variants_stripped: Boolean! - # edges from Item - span: Span + # edges from Item + span: Span + attribute: [Attribute!]! - # edges from Importable - path: [Path!] + # edges from Importable + path: [Path!] - # edges from ImplOwner - """ - Any impl for this type. + # edges from ImplOwner + """ + Any impl for this type. - All impl kinds are included: - - inherent impls: `impl Foo` - - explicit trait implementations: `impl Bar for Foo` - - blanket implementations: `impl Bar for T` - """ - impl: [Impl!] + All impl kinds are included: + - inherent impls: `impl Foo` + - explicit trait implementations: `impl Bar for Foo` + - blanket implementations: `impl Bar for T` + """ + impl: [Impl!] - """ - Only inherent impls: implementations of the type itself (`impl Foo`). + """ + Only inherent impls: implementations of the type itself (`impl Foo`). - The impls pointed to here are guaranteed to have no `trait` and no `blanket` edges. + The impls pointed to here are guaranteed to have no `trait` and no `blanket` edges. - This edge is just a convenience to simplify query-writing, - so we don't have to keep writing "@fold @transform(...) @filter(...)" chains - over the `trait` and `blanket` edges. + This edge is just a convenience to simplify query-writing, + so we don't have to keep writing "@fold @transform(...) @filter(...)" chains + over the `trait` and `blanket` edges. - When Trustfall supports macro edges, this should just become a macro edge. - """ - inherent_impl: [Impl!] + When Trustfall supports macro edges, this should just become a macro edge. + """ + inherent_impl: [Impl!] - # own edges - variant: [Variant!] + # own edges + variant: [Variant!] } """ @@ -178,16 +188,17 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.Variant.html """ interface Variant implements Item { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! + visibility_limit: String! + + # edges from Item + span: Span + attribute: [Attribute!]! } """ @@ -196,16 +207,17 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.Variant.html """ type PlainVariant implements Item & Variant { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! + visibility_limit: String! + + # edges from Item + span: Span + attribute: [Attribute!]! } """ @@ -214,16 +226,17 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.Variant.html """ type TupleVariant implements Item & Variant { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! + visibility_limit: String! + + # edges from Item + span: Span + attribute: [Attribute!]! } """ @@ -232,77 +245,79 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.Variant.html """ type StructVariant implements Item & Variant { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! + visibility_limit: String! + + # edges from Item + span: Span + attribute: [Attribute!]! } """ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Span.html """ type Span { - filename: String! - begin_line: Int! - begin_column: Int! - end_line: Int! - end_column: Int! + filename: String! + begin_line: Int! + begin_column: Int! + end_line: Int! + end_column: Int! } """ An item that can be imported, through one or more paths. """ interface Importable { - path: [Path!] + path: [Path!] } """ An item that can have impl blocks, like a struct or enum. """ interface ImplOwner implements Item & Importable { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # edges from Item - span: Span - - # edges from Importable - path: [Path!] - - # own edges - """ - Any impl for this type. - - All impl kinds are included: - - inherent impls: `impl Foo` - - explicit trait implementations: `impl Bar for Foo` - - blanket implementations: `impl Bar for T` - """ - impl: [Impl!] - - """ - Only inherent impls: implementations of the type itself (`impl Foo`). - - The impls pointed to here are guaranteed to have no `trait` and no `blanket` edges. - - This edge is just a convenience to simplify query-writing, - so we don't have to keep writing "@fold @transform(...) @filter(...)" chains - over the `trait` and `blanket` edges. - - When Trustfall supports macro edges, this should just become a macro edge. - """ - inherent_impl: [Impl!] + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! + visibility_limit: String! + + # edges from Item + span: Span + attribute: [Attribute!]! + + # edges from Importable + path: [Path!] + + # own edges + """ + Any impl for this type. + + All impl kinds are included: + - inherent impls: `impl Foo` + - explicit trait implementations: `impl Bar for Foo` + - blanket implementations: `impl Bar for T` + """ + impl: [Impl!] + + """ + Only inherent impls: implementations of the type itself (`impl Foo`). + + The impls pointed to here are guaranteed to have no `trait` and no `blanket` edges. + + This edge is just a convenience to simplify query-writing, + so we don't have to keep writing "@fold @transform(...) @filter(...)" chains + over the `trait` and `blanket` edges. + + When Trustfall supports macro edges, this should just become a macro edge. + """ + inherent_impl: [Impl!] } """ @@ -311,54 +326,55 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html https://docs.rs/rustdoc-types/latest/rustdoc_types/struct.Impl.html """ type Impl implements Item { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! - # stringified version of the visibility struct field - visibility_limit: String! + # stringified version of the visibility struct field + visibility_limit: String! - # own properties - unsafe: Boolean! - negative: Boolean! - synthetic: Boolean! + # own properties + unsafe: Boolean! + negative: Boolean! + synthetic: Boolean! - # edges from Item - span: Span + # edges from Item + span: Span + attribute: [Attribute!]! - # own edges + # own edges - # """ - # The trait being implemented. Inherent impls don't have a trait. + # """ + # The trait being implemented. Inherent impls don't have a trait. - # TODO: implement me - # """ - # trait: Trait + # TODO: implement me + # """ + # trait: Trait - # """ - # The generic type across which the blanket trait implementation is made. + # """ + # The generic type across which the blanket trait implementation is made. - # TODO: implement me - # """ - # blanket: GenericType + # TODO: implement me + # """ + # blanket: GenericType - """ - Methods defined in this impl. - """ - method: [Method!] + """ + Methods defined in this impl. + """ + method: [Method!] } """ A possible way that an item could be imported. """ type Path { - """ - The path from which the item can be imported. - """ - path: [String!]! + """ + The path from which the item can be imported. + """ + path: [String!]! } """ @@ -369,9 +385,9 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Header.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.FnDecl.html """ interface FunctionLike { - const: Boolean! - unsafe: Boolean! - async: Boolean! + const: Boolean! + unsafe: Boolean! + async: Boolean! } """ @@ -380,24 +396,25 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Function.html """ type Function implements Item & FunctionLike & Importable { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # properties from FunctionLike - const: Boolean! - unsafe: Boolean! - async: Boolean! - - # edges from Item - span: Span - - # edges from Importable - path: [Path!] + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! + visibility_limit: String! + + # properties from FunctionLike + const: Boolean! + unsafe: Boolean! + async: Boolean! + + # edges from Item + span: Span + attribute: [Attribute!]! + + # edges from Importable + path: [Path!] } """ @@ -406,21 +423,34 @@ https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/enum.ItemEnum.html https://docs.rs/rustdoc-types/0.11.0/rustdoc_types/struct.Method.html """ type Method implements Item & FunctionLike { - # properties from Item - id: String! - crate_id: Int! - name: String - docs: String - attrs: [String!]! - visibility_limit: String! - - # properties from FunctionLike - const: Boolean! - unsafe: Boolean! - async: Boolean! - - # edge from Item - span: Span + # properties from Item + id: String! + crate_id: Int! + name: String + docs: String + attrs: [String!]! + visibility_limit: String! + + # properties from FunctionLike + const: Boolean! + unsafe: Boolean! + async: Boolean! + + # edge from Item + span: Span + attribute: [Attribute!]! +} + +""" +A specific attribute applied to an Item. +""" +type Attribute { + """ + The textual representation of the attribute. + + For example: "#[repr(C)]" + """ + value: String! } """ diff --git a/src/test_data/enum_repr_int_changed.output.ron b/src/test_data/enum_repr_int_changed.output.ron new file mode 100644 index 00000000..c8a8909c --- /dev/null +++ b/src/test_data/enum_repr_int_changed.output.ron @@ -0,0 +1,50 @@ +[ + { + "name": String("U8ToU16Enum"), + "path": List([ + String("semver_tests"), + String("test_cases"), + String("enum_repr_int_changed"), + String("U8ToU16Enum"), + ]), + "span_filename": String("src/test_cases/enum_repr_int_changed.rs"), + "span_begin_line": Uint64(10), + "old_attr": String("#[repr(u8)]"), + "new_attr": List([ + String("#[repr(u16)]"), + ]), + "visibility_limit": String("public"), + }, + { + "name": String("I32ToI8Enum"), + "path": List([ + String("semver_tests"), + String("test_cases"), + String("enum_repr_int_changed"), + String("I32ToI8Enum"), + ]), + "span_filename": String("src/test_cases/enum_repr_int_changed.rs"), + "span_begin_line": Uint64(24), + "old_attr": String("#[repr(i32)]"), + "new_attr": List([ + String("#[repr(i8)]"), + ]), + "visibility_limit": String("public"), + }, + { + "name": String("I32ToU32Enum"), + "path": List([ + String("semver_tests"), + String("test_cases"), + String("enum_repr_int_changed"), + String("I32ToU32Enum"), + ]), + "span_filename": String("src/test_cases/enum_repr_int_changed.rs"), + "span_begin_line": Uint64(38), + "old_attr": String("#[repr(i32)]"), + "new_attr": List([ + String("#[repr(u32)]"), + ]), + "visibility_limit": String("public"), + } +] diff --git a/src/test_data/enum_repr_int_removed.output.ron b/src/test_data/enum_repr_int_removed.output.ron new file mode 100644 index 00000000..53cf128e --- /dev/null +++ b/src/test_data/enum_repr_int_removed.output.ron @@ -0,0 +1,26 @@ +[ + { + "name": String("U8Enum"), + "path": List([ + String("semver_tests"), + String("test_cases"), + String("enum_repr_int_removed"), + String("U8Enum"), + ]), + "span_filename": String("src/test_cases/enum_repr_int_removed.rs"), + "span_begin_line": Uint64(9), + "visibility_limit": String("public"), + }, + { + "name": String("I32Enum"), + "path": List([ + String("semver_tests"), + String("test_cases"), + String("enum_repr_int_removed"), + String("I32Enum"), + ]), + "span_filename": String("src/test_cases/enum_repr_int_removed.rs"), + "span_begin_line": Uint64(22), + "visibility_limit": String("public"), + } +]