1use std::ffi::OsStr;
4use std::fmt;
5use std::fmt::Display;
6use std::ops::Deref;
7
8use super::BtfKind;
9use super::BtfType;
10use super::HasSize;
11use super::ReferencesType;
12use super::TypeId;
13
14macro_rules! gen_fieldless_concrete_type {
17 (
18 $(#[$docs:meta])*
19 $name:ident $(with $trait:ident)?
20 ) => {
21 $(#[$docs])*
22 #[derive(Clone, Copy, Debug)]
23 pub struct $name<'btf> {
24 source: BtfType<'btf>,
25 }
26
27 impl<'btf> TryFrom<BtfType<'btf>> for $name<'btf> {
28 type Error = BtfType<'btf>;
29
30 fn try_from(t: BtfType<'btf>) -> ::core::result::Result<Self, Self::Error> {
31 if t.kind() == BtfKind::$name {
32 Ok($name { source: t })
33 } else {
34 Err(t)
35 }
36 }
37 }
38
39 impl<'btf> ::std::ops::Deref for $name<'btf> {
40 type Target = BtfType<'btf>;
41 fn deref(&self) -> &Self::Target {
42 &self.source
43 }
44 }
45
46 $(
47 impl super::sealed::Sealed for $name<'_> {}
48 unsafe impl<'btf> $trait<'btf> for $name<'btf> {}
49 )*
50 };
51}
52
53macro_rules! gen_concrete_type {
56 (
57 $(#[$docs:meta])*
58 $libbpf_ty:ident as $name:ident $(with $trait:ident)?
59 ) => {
60 $(#[$docs])*
61 #[derive(Clone, Copy, Debug)]
62 pub struct $name<'btf> {
63 source: BtfType<'btf>,
64 ptr: &'btf libbpf_sys::$libbpf_ty,
65 }
66
67 impl<'btf> TryFrom<BtfType<'btf>> for $name<'btf> {
68 type Error = BtfType<'btf>;
69
70 fn try_from(t: BtfType<'btf>) -> ::core::result::Result<Self, Self::Error> {
71 if t.kind() == BtfKind::$name {
72 let ptr = unsafe {
73 (t.ty as *const libbpf_sys::btf_type).offset(1)
78 };
79 let ptr = ptr.cast::<libbpf_sys::$libbpf_ty>();
80 Ok($name {
81 source: t,
82 ptr: unsafe { &*ptr },
93 })
94 } else {
95 Err(t)
96 }
97 }
98 }
99
100 impl<'btf> ::std::ops::Deref for $name<'btf> {
101 type Target = BtfType<'btf>;
102 fn deref(&self) -> &Self::Target {
103 &self.source
104 }
105 }
106
107 $(
108 impl super::sealed::Sealed for $name<'_> {}
109 unsafe impl<'btf> $trait<'btf> for $name<'btf> {}
110 )*
111 };
112}
113
114macro_rules! gen_collection_members_concrete_type {
115 (
116 $libbpf_ty:ident as $name:ident $(with $trait:ident)?;
117
118 $(#[$docs:meta])*
119 struct $member_name:ident $(<$lt:lifetime>)? {
120 $(
121 $(#[$field_docs:meta])*
122 pub $field:ident : $type:ty
123 ),* $(,)?
124 }
125
126 |$btf:ident, $member:ident $(, $kind_flag:ident)?| $convert:expr
127 ) => {
128 impl<'btf> ::std::ops::Deref for $name<'btf> {
129 type Target = BtfType<'btf>;
130 fn deref(&self) -> &Self::Target {
131 &self.source
132 }
133 }
134
135 impl<'btf> $name<'btf> {
136 #[inline]
138 pub fn is_empty(&self) -> bool {
139 self.members.is_empty()
140 }
141
142 #[doc = ::core::concat!("How many members this [`", ::core::stringify!($name), "`] has")]
143 #[inline]
144 pub fn len(&self) -> usize {
145 self.members.len()
146 }
147
148 #[doc = ::core::concat!("Get a [`", ::core::stringify!($member_name), "`] at a given index")]
149 pub fn get(&self, index: usize) -> Option<$member_name$(<$lt>)*> {
153 self.members.get(index).map(|m| self.c_to_rust_member(m))
154 }
155
156 #[doc = ::core::concat!("Returns an iterator over the [`", ::core::stringify!($member_name), "`]'s of the [`", ::core::stringify!($name), "`]")]
157 pub fn iter(&'btf self) -> impl ExactSizeIterator<Item = $member_name$(<$lt>)*> + 'btf {
158 self.members.iter().map(|m| self.c_to_rust_member(m))
159 }
160
161 fn c_to_rust_member(&self, member: &libbpf_sys::$libbpf_ty) -> $member_name$(<$lt>)* {
162 let $btf = self.source.source;
163 let $member = member;
164 $(let $kind_flag = self.source.kind_flag();)*
165 $convert
166 }
167 }
168
169 $(#[$docs])*
170 #[derive(Clone, Copy, Debug)]
171 pub struct $member_name $(<$lt>)? {
172 $(
173 $(#[$field_docs])*
174 pub $field: $type
175 ),*
176 }
177
178 $(
179 impl $crate::btf::sealed::Sealed for $name<'_> {}
180 unsafe impl<'btf> $trait<'btf> for $name<'btf> {}
181 )*
182 };
183}
184
185macro_rules! gen_collection_concrete_type {
186 (
187 $(#[$docs:meta])*
188 $libbpf_ty:ident as $name:ident $(with $trait:ident)?;
189
190 $($rest:tt)+
191 ) => {
192 $(#[$docs])*
193 #[derive(Clone, Copy, Debug)]
194 pub struct $name<'btf> {
195 source: BtfType<'btf>,
196 members: &'btf [libbpf_sys::$libbpf_ty],
197 }
198
199 impl<'btf> TryFrom<BtfType<'btf>> for $name<'btf> {
200 type Error = BtfType<'btf>;
201
202 fn try_from(t: BtfType<'btf>) -> ::core::result::Result<Self, Self::Error> {
203 if t.kind() == BtfKind::$name {
204 let base_ptr = unsafe {
205 (t.ty as *const libbpf_sys::btf_type).offset(1)
210 };
211 let members = unsafe {
212 std::slice::from_raw_parts(base_ptr.cast(), t.vlen() as usize)
225 };
226 Ok(Self { source: t, members })
227 } else {
228 Err(t)
229 }
230 }
231 }
232
233 gen_collection_members_concrete_type!{
234 $libbpf_ty as $name $(with $trait)?;
235 $($rest)*
236 }
237 };
238}
239
240#[derive(Clone, Copy, Debug)]
242pub enum MemberAttr {
243 Normal {
245 offset: u32,
247 },
248 BitField {
250 size: u8,
252 offset: u32,
254 },
255}
256
257impl MemberAttr {
258 #[inline]
259 fn new(kflag: bool, offset: u32) -> Self {
260 if kflag {
261 let size = (offset >> 24) as u8;
262 if size != 0 {
263 Self::BitField {
264 size,
265 offset: offset & 0x00_ff_ff_ff,
266 }
267 } else {
268 Self::Normal { offset }
269 }
270 } else {
271 Self::Normal { offset }
272 }
273 }
274}
275
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
278#[repr(u32)]
279pub enum Linkage {
280 Static = 0,
282 Global,
284 Extern,
286 Unknown,
288}
289
290impl From<u32> for Linkage {
291 fn from(value: u32) -> Self {
292 use Linkage::*;
293
294 match value {
295 x if x == Static as u32 => Static,
296 x if x == Global as u32 => Global,
297 x if x == Extern as u32 => Extern,
298 _ => Unknown,
299 }
300 }
301}
302
303impl From<Linkage> for u32 {
304 fn from(value: Linkage) -> Self {
305 value as u32
306 }
307}
308
309impl Display for Linkage {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 write!(
312 f,
313 "{}",
314 match self {
315 Linkage::Static => "static",
316 Linkage::Global => "global",
317 Linkage::Extern => "extern",
318 Linkage::Unknown => "(unknown)",
319 }
320 )
321 }
322}
323
324gen_fieldless_concrete_type! {
326 Void
328}
329
330#[derive(Clone, Copy, Debug)]
336pub struct Int<'btf> {
337 source: BtfType<'btf>,
338 pub encoding: IntEncoding,
340 pub offset: u8,
343 pub bits: u8,
345}
346
347#[derive(Clone, Copy, Debug)]
349pub enum IntEncoding {
350 None,
352 Signed,
354 Char,
356 Bool,
358}
359
360impl<'btf> TryFrom<BtfType<'btf>> for Int<'btf> {
361 type Error = BtfType<'btf>;
362
363 fn try_from(t: BtfType<'btf>) -> Result<Self, Self::Error> {
364 if t.kind() == BtfKind::Int {
365 let int = {
366 let base_ptr = t.ty as *const libbpf_sys::btf_type;
367 let u32_ptr = unsafe {
368 base_ptr.offset(1).cast::<u32>()
373 };
374 unsafe {
375 *u32_ptr
386 }
387 };
388 let encoding = match (int & 0x0f_00_00_00) >> 24 {
389 0b1 => IntEncoding::Signed,
390 0b10 => IntEncoding::Char,
391 0b100 => IntEncoding::Bool,
392 _ => IntEncoding::None,
393 };
394 Ok(Self {
395 source: t,
396 encoding,
397 offset: ((int & 0x00_ff_00_00) >> 24) as u8,
398 bits: (int & 0x00_00_00_ff) as u8,
399 })
400 } else {
401 Err(t)
402 }
403 }
404}
405
406impl<'btf> Deref for Int<'btf> {
407 type Target = BtfType<'btf>;
408 fn deref(&self) -> &Self::Target {
409 &self.source
410 }
411}
412
413impl super::sealed::Sealed for Int<'_> {}
415unsafe impl<'btf> HasSize<'btf> for Int<'btf> {}
416
417gen_fieldless_concrete_type! {
419 Ptr with ReferencesType
423}
424
425gen_concrete_type! {
427 btf_array as Array
431}
432
433impl<'s> Array<'s> {
434 #[inline]
436 pub fn ty(&self) -> TypeId {
437 self.ptr.type_.into()
438 }
439
440 #[inline]
442 pub fn index_ty(&self) -> TypeId {
443 self.ptr.index_type.into()
444 }
445
446 #[inline]
448 pub fn capacity(&self) -> usize {
449 self.ptr.nelems as usize
450 }
451
452 #[inline]
454 pub fn contained_type(&self) -> BtfType<'s> {
455 self.source
456 .source
457 .type_by_id(self.ty())
458 .expect("arrays should always reference an existing type")
459 }
460}
461
462gen_collection_concrete_type! {
464 btf_member as Struct with HasSize;
468
469 struct StructMember<'btf> {
471 pub name: Option<&'btf OsStr>,
473 pub ty: TypeId,
475 pub attr: MemberAttr,
477 }
478
479 |btf, member, kflag| StructMember {
480 name: btf.name_at(member.name_off),
481 ty: member.type_.into(),
482 attr: MemberAttr::new(kflag, member.offset),
483 }
484}
485
486gen_collection_concrete_type! {
488 btf_member as Union with HasSize;
492
493 struct UnionMember<'btf> {
495 pub name: Option<&'btf OsStr>,
497 pub ty: TypeId,
499 pub attr: MemberAttr,
501 }
502
503 |btf, member, kflag| UnionMember {
504 name: btf.name_at(member.name_off),
505 ty: member.type_.into(),
506 attr: MemberAttr::new(kflag, member.offset),
507 }
508}
509
510#[derive(Clone, Copy, Debug)]
515pub struct Composite<'btf> {
516 source: BtfType<'btf>,
517 pub is_struct: bool,
519 members: &'btf [libbpf_sys::btf_member],
520}
521
522impl<'btf> From<Struct<'btf>> for Composite<'btf> {
523 fn from(s: Struct<'btf>) -> Self {
524 Self {
525 source: s.source,
526 is_struct: true,
527 members: s.members,
528 }
529 }
530}
531
532impl<'btf> From<Union<'btf>> for Composite<'btf> {
533 fn from(s: Union<'btf>) -> Self {
534 Self {
535 source: s.source,
536 is_struct: false,
537 members: s.members,
538 }
539 }
540}
541
542impl<'btf> TryFrom<BtfType<'btf>> for Composite<'btf> {
543 type Error = BtfType<'btf>;
544
545 fn try_from(t: BtfType<'btf>) -> Result<Self, Self::Error> {
546 Struct::try_from(t)
547 .map(Self::from)
548 .or_else(|_| Union::try_from(t).map(Self::from))
549 }
550}
551
552impl<'btf> TryFrom<Composite<'btf>> for Struct<'btf> {
553 type Error = Composite<'btf>;
554
555 fn try_from(value: Composite<'btf>) -> Result<Self, Self::Error> {
556 if value.is_struct {
557 Ok(Self {
558 source: value.source,
559 members: value.members,
560 })
561 } else {
562 Err(value)
563 }
564 }
565}
566
567impl<'btf> TryFrom<Composite<'btf>> for Union<'btf> {
568 type Error = Composite<'btf>;
569
570 fn try_from(value: Composite<'btf>) -> Result<Self, Self::Error> {
571 if !value.is_struct {
572 Ok(Self {
573 source: value.source,
574 members: value.members,
575 })
576 } else {
577 Err(value)
578 }
579 }
580}
581
582impl Composite<'_> {
583 pub fn is_empty_union(&self) -> bool {
585 !self.is_struct && self.is_empty()
586 }
587}
588
589gen_collection_members_concrete_type! {
591 btf_member as Composite with HasSize;
592
593 struct CompositeMember<'btf> {
595 pub name: Option<&'btf OsStr>,
597 pub ty: TypeId,
599 pub attr: MemberAttr
601 }
602
603 |btf, member, kflag| CompositeMember {
604 name: btf.name_at(member.name_off),
605 ty: member.type_.into(),
606 attr: MemberAttr::new(kflag, member.offset),
607 }
608}
609
610gen_collection_concrete_type! {
612 btf_enum as Enum with HasSize;
616
617 struct EnumMember<'btf> {
619 pub name: Option<&'btf OsStr>,
621 pub value: i64,
623 }
624
625 |btf, member, signed| {
626 EnumMember {
627 name: btf.name_at(member.name_off),
628 value: if signed {
629 member.val.into()
630 } else {
631 u32::from_ne_bytes(member.val.to_ne_bytes()).into()
632 }
633 }
634 }
635}
636
637impl Enum<'_> {
638 #[inline]
640 pub fn is_signed(&self) -> bool {
641 self.kind_flag()
642 }
643}
644
645
646gen_fieldless_concrete_type! {
648 Fwd
652}
653
654impl Fwd<'_> {
655 pub fn kind(&self) -> FwdKind {
657 if self.source.kind_flag() {
658 FwdKind::Union
659 } else {
660 FwdKind::Struct
661 }
662 }
663}
664
665#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
667pub enum FwdKind {
668 Struct,
670 Union,
672}
673
674gen_fieldless_concrete_type! {
676 Typedef with ReferencesType
682}
683
684gen_fieldless_concrete_type! {
686 Volatile with ReferencesType
690}
691
692gen_fieldless_concrete_type! {
694 Const with ReferencesType
698}
699
700gen_fieldless_concrete_type! {
702 Restrict with ReferencesType
706}
707
708gen_fieldless_concrete_type! {
710 Func with ReferencesType
714}
715
716impl Func<'_> {
717 #[inline]
719 pub fn linkage(&self) -> Linkage {
720 self.source.vlen().into()
721 }
722}
723
724gen_collection_concrete_type! {
726 btf_param as FuncProto with ReferencesType;
730
731 struct FuncProtoParam<'btf> {
733 pub name: Option<&'btf OsStr>,
735 pub ty: TypeId,
737 }
738
739 |btf, member| FuncProtoParam {
740 name: btf.name_at(member.name_off),
741 ty: member.type_.into()
742 }
743}
744
745gen_concrete_type! {
747 btf_var as Var with ReferencesType
751}
752
753impl Var<'_> {
754 #[inline]
756 pub fn linkage(&self) -> Linkage {
757 self.ptr.linkage.into()
758 }
759}
760
761gen_collection_concrete_type! {
763 btf_var_secinfo as DataSec with HasSize;
767
768 struct VarSecInfo {
772 pub ty: TypeId,
774 pub offset: u32,
776 pub size: usize,
778 }
779
780 |_btf, member| VarSecInfo {
781 ty: member.type_.into(),
782 offset: member.offset,
783 size: member.size as usize
784 }
785}
786
787gen_fieldless_concrete_type! {
789 Float with HasSize
793}
794
795gen_concrete_type! {
797 btf_decl_tag as DeclTag with ReferencesType
805}
806
807impl DeclTag<'_> {
808 #[inline]
812 pub fn component_index(&self) -> Option<u32> {
813 self.ptr.component_idx.try_into().ok()
814 }
815}
816
817gen_fieldless_concrete_type! {
819 TypeTag with ReferencesType
823}
824
825gen_collection_concrete_type! {
827 btf_enum64 as Enum64 with HasSize;
831
832 struct Enum64Member<'btf> {
834 pub name: Option<&'btf OsStr>,
836 pub value: i128,
838 }
839
840 |btf, member, signed| Enum64Member {
841 name: btf.name_at(member.name_off),
842 value: {
843 let hi: u64 = member.val_hi32.into();
844 let lo: u64 = member.val_lo32.into();
845 let val = (hi << 32) | lo;
846 if signed {
847 i64::from_ne_bytes(val.to_ne_bytes()).into()
848 } else {
849 val.into()
850 }
851 },
852 }
853}
854
855impl Enum64<'_> {
856 #[inline]
858 pub fn is_signed(&self) -> bool {
859 self.kind_flag()
860 }
861}
862
863
864#[macro_export]
918macro_rules! btf_type_match {
919 (
921 match $ty:ident {
922 $($pattern:tt)+
923 }
924 ) => {{
925 let ty: $crate::btf::BtfType<'_> = $ty;
926 $crate::__btf_type_match!(match ty.kind() { } $($pattern)*)
927 }};
928}
929
930#[doc(hidden)]
931#[macro_export]
932macro_rules! __btf_type_match {
933 (
940 match $ty:ident.kind() { $($p:pat => $a:expr),* }
941 BtfKind::Composite $( ($var:ident) )? => $action:expr,
942 $($rest:tt)*
943 ) => {
944 $crate::__btf_type_match!(match $ty.kind() { $($p => $a,)* }
945 BtfKind::Composite $( ($var) )* => { $action }
946 $($rest)*
947 )
948 };
949 (
950 match $ty:ident.kind() { $($p:pat => $a:expr),* }
951 BtfKind::Composite $(($var:ident))? => $action:block
952 $($rest:tt)*
953 ) => {
954 $crate::__btf_type_match!(match $ty.kind() {
955 $($p => $a,)*
956 $crate::btf::BtfKind::Struct | $crate::btf::BtfKind::Union => {
957 $(let $var = $crate::btf::types::Composite::try_from($ty).unwrap();)*
958 $action
959 }
960 }
961 $($rest)*
962 )
963 };
964 (
966 match $ty:ident.kind() { $($p:pat => $a:expr),* }
967 BtfKind::$name:ident $(($var:ident))? => $action:expr,
968 $($rest:tt)*
969 ) => {
970 $crate::__btf_type_match!(
971 match $ty.kind() { $($p => $a),* }
972 BtfKind::$name $(($var))? => { $action }
973 $($rest)*
974 )
975 };
976 (
978 match $ty:ident.kind() { $($p:pat => $a:expr),* }
979 BtfKind::$name:ident $(($var:ident))? => $action:block
980 $($rest:tt)*
981 ) => {
982 $crate::__btf_type_match!(match $ty.kind() {
983 $($p => $a,)*
984 $crate::btf::BtfKind::$name => {
985 $(let $var = $crate::btf::types::$name::try_from($ty).unwrap();)*
986 $action
987 }
988 }
989 $($rest)*
990 )
991 };
992 (
995 match $ty:ident.kind() { $($p:pat => $a:expr),* }
996 $(BtfKind::$name:ident)|+ => $action:expr,
997 $($rest:tt)*
998 ) => {
999 $crate::__btf_type_match!(
1000 match $ty.kind() { $($p => $a),* }
1001 $(BtfKind::$name)|* => { $action }
1002 $($rest)*
1003 )
1004 };
1005 (
1006 match $ty:ident.kind() { $($p:pat => $a:expr),* }
1007 $(BtfKind::$name:ident)|+ => $action:block
1008 $($rest:tt)*
1009 ) => {
1010 $crate::__btf_type_match!(match $ty.kind() {
1011 $($p => $a,)*
1012 $($crate::btf::BtfKind::$name)|* => {
1013 $action
1014 }
1015 }
1016 $($rest)*
1017 )
1018 };
1019 (
1024 match $ty:ident.kind() { $($p:pat => $a:expr),* }
1025 _ => $action:expr $(,)?
1026 ) => {
1027 $crate::__btf_type_match!(match $ty.kind() {
1028 $($p => $a,)*
1029 _ => { $action }
1030 }
1031
1032 )
1033 };
1034 (match $ty:ident.kind() { $($p:pat => $a:expr),* } ) => {
1036 match $ty.kind() {
1037 $($p => $a),*
1038 }
1039 }
1040}
1041
1042#[cfg(test)]
1043mod test {
1044 use super::*;
1045
1046 macro_rules! dummy_type {
1049 ($ty:ident) => {
1050 let btf = $crate::Btf {
1051 ptr: std::ptr::NonNull::dangling(),
1052 drop_policy: $crate::btf::DropPolicy::Nothing,
1053 _marker: std::marker::PhantomData,
1054 };
1055 let $ty = BtfType {
1056 type_id: $crate::btf::TypeId::from(1),
1057 name: None,
1058 source: &btf,
1059 ty: &libbpf_sys::btf_type::default(),
1060 };
1061 };
1062 }
1063
1064 fn foo(_: super::Int<'_>) -> &'static str {
1065 "int"
1066 }
1067
1068 #[test]
1069 fn full_switch_case() {
1070 dummy_type!(ty);
1071 btf_type_match!(match ty {
1072 BtfKind::Int(i) => foo(i),
1073 BtfKind::Struct => "it's a struct",
1074 BtfKind::Void => "",
1075 BtfKind::Ptr => "",
1076 BtfKind::Array => "",
1077 BtfKind::Union => "",
1078 BtfKind::Enum => "",
1079 BtfKind::Fwd => "",
1080 BtfKind::Typedef => "",
1081 BtfKind::Volatile => "",
1082 BtfKind::Const => "",
1083 BtfKind::Restrict => "",
1084 BtfKind::Func => "",
1085 BtfKind::FuncProto => "",
1086 BtfKind::Var => "",
1087 BtfKind::DataSec => "",
1088 BtfKind::Float => "",
1089 BtfKind::DeclTag => "",
1090 BtfKind::TypeTag => "",
1091 BtfKind::Enum64 => "",
1092 });
1093 }
1094
1095 #[test]
1096 fn partial_match() {
1097 dummy_type!(ty);
1098 btf_type_match!(match ty {
1099 BtfKind::Int => "int",
1100 _ => "default",
1101 });
1102 }
1103
1104 #[test]
1105 fn or_pattern_match() {
1106 dummy_type!(ty);
1107 #[rustfmt::skip]
1110 btf_type_match!(match ty {
1111 BtfKind::Int => "int",
1112 BtfKind::Struct | BtfKind::Union => "composite",
1113 BtfKind::Typedef | BtfKind::Volatile => {
1114 "qualifier"
1115 }
1116 BtfKind::Const | BtfKind::Restrict => {
1117 "const or restrict"
1118 },
1119 _ => "default",
1120 });
1121 }
1122
1123 #[test]
1124 fn match_arm_with_brackets() {
1125 dummy_type!(ty);
1126 #[rustfmt::skip]
1129 btf_type_match!(match ty {
1130 BtfKind::Void => {
1131 "void"
1132 }
1133 BtfKind::Int => {
1134 "int"
1135 },
1136 BtfKind::Struct => "struct",
1137 _ => "default",
1138 });
1139 }
1140
1141 #[test]
1142 fn match_on_composite() {
1143 dummy_type!(ty);
1144 btf_type_match!(match ty {
1145 BtfKind::Composite(c) => c.is_struct,
1146 _ => false,
1147 });
1148 btf_type_match!(match ty {
1149 BtfKind::Composite(c) => {
1150 c.is_struct
1151 }
1152 _ => false,
1153 });
1154 #[rustfmt::skip]
1157 btf_type_match!(match ty {
1158 BtfKind::Composite(c) => {
1159 c.is_struct
1160 },
1161 _ => false,
1162 });
1163 }
1164
1165 #[test]
1166 fn match_arm_with_multiple_statements() {
1167 dummy_type!(ty);
1168
1169 btf_type_match!(match ty {
1170 BtfKind::Int(i) => {
1171 let _ = i;
1172 "int"
1173 }
1174 _ => {
1175 let _ = 1;
1176 "default"
1177 }
1178 });
1179 }
1180
1181 #[test]
1182 fn non_expression_guards() {
1183 dummy_type!(ty);
1184
1185 btf_type_match!(match ty {
1186 BtfKind::Int => {
1187 let _ = 1;
1188 "int"
1189 }
1190 BtfKind::Typedef | BtfKind::Const => {
1191 let _ = 1;
1192 "qualifier"
1193 }
1194 _ => {
1195 let _ = 1;
1196 "default"
1197 }
1198 });
1199
1200 btf_type_match!(match ty {
1201 BtfKind::Int => {
1202 let _ = 1;
1203 }
1204 BtfKind::Typedef | BtfKind::Const => {
1205 let _ = 1;
1206 }
1207 _ => {
1208 let _ = 1;
1209 }
1210 });
1211 }
1212
1213 #[test]
1214 fn linkage_type() {
1215 use std::mem::discriminant;
1216 use Linkage::*;
1217
1218 for t in [Static, Global, Extern, Unknown] {
1219 assert_eq!(discriminant(&t), discriminant(&Linkage::from(t as u32)));
1221 }
1222 }
1223}