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: i32,
623 }
624
625 |btf, member| EnumMember {
626 name: btf.name_at(member.name_off),
627 value: member.val,
628 }
629}
630
631gen_fieldless_concrete_type! {
633 Fwd
637}
638
639impl Fwd<'_> {
640 pub fn kind(&self) -> FwdKind {
642 if self.source.kind_flag() {
643 FwdKind::Union
644 } else {
645 FwdKind::Struct
646 }
647 }
648}
649
650#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
652pub enum FwdKind {
653 Struct,
655 Union,
657}
658
659gen_fieldless_concrete_type! {
661 Typedef with ReferencesType
667}
668
669gen_fieldless_concrete_type! {
671 Volatile with ReferencesType
675}
676
677gen_fieldless_concrete_type! {
679 Const with ReferencesType
683}
684
685gen_fieldless_concrete_type! {
687 Restrict with ReferencesType
691}
692
693gen_fieldless_concrete_type! {
695 Func with ReferencesType
699}
700
701impl Func<'_> {
702 #[inline]
704 pub fn linkage(&self) -> Linkage {
705 self.source.vlen().into()
706 }
707}
708
709gen_collection_concrete_type! {
711 btf_param as FuncProto with ReferencesType;
715
716 struct FuncProtoParam<'btf> {
718 pub name: Option<&'btf OsStr>,
720 pub ty: TypeId,
722 }
723
724 |btf, member| FuncProtoParam {
725 name: btf.name_at(member.name_off),
726 ty: member.type_.into()
727 }
728}
729
730gen_concrete_type! {
732 btf_var as Var with ReferencesType
736}
737
738impl Var<'_> {
739 #[inline]
741 pub fn linkage(&self) -> Linkage {
742 self.ptr.linkage.into()
743 }
744}
745
746gen_collection_concrete_type! {
748 btf_var_secinfo as DataSec with HasSize;
752
753 struct VarSecInfo {
757 pub ty: TypeId,
759 pub offset: u32,
761 pub size: usize,
763 }
764
765 |_btf, member| VarSecInfo {
766 ty: member.type_.into(),
767 offset: member.offset,
768 size: member.size as usize
769 }
770}
771
772gen_fieldless_concrete_type! {
774 Float with HasSize
778}
779
780gen_concrete_type! {
782 btf_decl_tag as DeclTag with ReferencesType
790}
791
792impl DeclTag<'_> {
793 #[inline]
797 pub fn component_index(&self) -> Option<u32> {
798 self.ptr.component_idx.try_into().ok()
799 }
800}
801
802gen_fieldless_concrete_type! {
804 TypeTag with ReferencesType
808}
809
810gen_collection_concrete_type! {
812 btf_enum64 as Enum64 with HasSize;
816
817 struct Enum64Member<'btf> {
819 pub name: Option<&'btf OsStr>,
821 pub value: u64,
823 }
824
825 |btf, member| Enum64Member {
826 name: btf.name_at(member.name_off),
827 value: {
828 let hi: u64 = member.val_hi32.into();
829 let lo: u64 = member.val_lo32.into();
830 hi << 32 | lo
831 },
832 }
833}
834
835#[macro_export]
889macro_rules! btf_type_match {
890 (
892 match $ty:ident {
893 $($pattern:tt)+
894 }
895 ) => {{
896 let ty: $crate::btf::BtfType<'_> = $ty;
897 $crate::__btf_type_match!(match ty.kind() { } $($pattern)*)
898 }};
899}
900
901#[doc(hidden)]
902#[macro_export]
903macro_rules! __btf_type_match {
904 (
911 match $ty:ident.kind() { $($p:pat => $a:expr),* }
912 BtfKind::Composite $( ($var:ident) )? => $action:expr,
913 $($rest:tt)*
914 ) => {
915 $crate::__btf_type_match!(match $ty.kind() { $($p => $a,)* }
916 BtfKind::Composite $( ($var) )* => { $action }
917 $($rest)*
918 )
919 };
920 (
921 match $ty:ident.kind() { $($p:pat => $a:expr),* }
922 BtfKind::Composite $(($var:ident))? => $action:block
923 $($rest:tt)*
924 ) => {
925 $crate::__btf_type_match!(match $ty.kind() {
926 $($p => $a,)*
927 $crate::btf::BtfKind::Struct | $crate::btf::BtfKind::Union => {
928 $(let $var = $crate::btf::types::Composite::try_from($ty).unwrap();)*
929 $action
930 }
931 }
932 $($rest)*
933 )
934 };
935 (
937 match $ty:ident.kind() { $($p:pat => $a:expr),* }
938 BtfKind::$name:ident $(($var:ident))? => $action:expr,
939 $($rest:tt)*
940 ) => {
941 $crate::__btf_type_match!(
942 match $ty.kind() { $($p => $a),* }
943 BtfKind::$name $(($var))? => { $action }
944 $($rest)*
945 )
946 };
947 (
949 match $ty:ident.kind() { $($p:pat => $a:expr),* }
950 BtfKind::$name:ident $(($var:ident))? => $action:block
951 $($rest:tt)*
952 ) => {
953 $crate::__btf_type_match!(match $ty.kind() {
954 $($p => $a,)*
955 $crate::btf::BtfKind::$name => {
956 $(let $var = $crate::btf::types::$name::try_from($ty).unwrap();)*
957 $action
958 }
959 }
960 $($rest)*
961 )
962 };
963 (
966 match $ty:ident.kind() { $($p:pat => $a:expr),* }
967 $(BtfKind::$name:ident)|+ => $action:expr,
968 $($rest:tt)*
969 ) => {
970 $crate::__btf_type_match!(
971 match $ty.kind() { $($p => $a),* }
972 $(BtfKind::$name)|* => { $action }
973 $($rest)*
974 )
975 };
976 (
977 match $ty:ident.kind() { $($p:pat => $a:expr),* }
978 $(BtfKind::$name:ident)|+ => $action:block
979 $($rest:tt)*
980 ) => {
981 $crate::__btf_type_match!(match $ty.kind() {
982 $($p => $a,)*
983 $($crate::btf::BtfKind::$name)|* => {
984 $action
985 }
986 }
987 $($rest)*
988 )
989 };
990 (
995 match $ty:ident.kind() { $($p:pat => $a:expr),* }
996 _ => $action:expr $(,)?
997 ) => {
998 $crate::__btf_type_match!(match $ty.kind() {
999 $($p => $a,)*
1000 _ => { $action }
1001 }
1002
1003 )
1004 };
1005 (match $ty:ident.kind() { $($p:pat => $a:expr),* } ) => {
1007 match $ty.kind() {
1008 $($p => $a),*
1009 }
1010 }
1011}
1012
1013#[cfg(test)]
1014mod test {
1015 use super::*;
1016
1017 macro_rules! dummy_type {
1020 ($ty:ident) => {
1021 let btf = $crate::Btf {
1022 ptr: std::ptr::NonNull::dangling(),
1023 drop_policy: $crate::btf::DropPolicy::Nothing,
1024 _marker: std::marker::PhantomData,
1025 };
1026 let $ty = BtfType {
1027 type_id: $crate::btf::TypeId::from(1),
1028 name: None,
1029 source: &btf,
1030 ty: &libbpf_sys::btf_type::default(),
1031 };
1032 };
1033 }
1034
1035 fn foo(_: super::Int<'_>) -> &'static str {
1036 "int"
1037 }
1038
1039 #[test]
1040 fn full_switch_case() {
1041 dummy_type!(ty);
1042 btf_type_match!(match ty {
1043 BtfKind::Int(i) => foo(i),
1044 BtfKind::Struct => "it's a struct",
1045 BtfKind::Void => "",
1046 BtfKind::Ptr => "",
1047 BtfKind::Array => "",
1048 BtfKind::Union => "",
1049 BtfKind::Enum => "",
1050 BtfKind::Fwd => "",
1051 BtfKind::Typedef => "",
1052 BtfKind::Volatile => "",
1053 BtfKind::Const => "",
1054 BtfKind::Restrict => "",
1055 BtfKind::Func => "",
1056 BtfKind::FuncProto => "",
1057 BtfKind::Var => "",
1058 BtfKind::DataSec => "",
1059 BtfKind::Float => "",
1060 BtfKind::DeclTag => "",
1061 BtfKind::TypeTag => "",
1062 BtfKind::Enum64 => "",
1063 });
1064 }
1065
1066 #[test]
1067 fn partial_match() {
1068 dummy_type!(ty);
1069 btf_type_match!(match ty {
1070 BtfKind::Int => "int",
1071 _ => "default",
1072 });
1073 }
1074
1075 #[test]
1076 fn or_pattern_match() {
1077 dummy_type!(ty);
1078 #[rustfmt::skip]
1081 btf_type_match!(match ty {
1082 BtfKind::Int => "int",
1083 BtfKind::Struct | BtfKind::Union => "composite",
1084 BtfKind::Typedef | BtfKind::Volatile => {
1085 "qualifier"
1086 }
1087 BtfKind::Const | BtfKind::Restrict => {
1088 "const or restrict"
1089 },
1090 _ => "default",
1091 });
1092 }
1093
1094 #[test]
1095 fn match_arm_with_brackets() {
1096 dummy_type!(ty);
1097 #[rustfmt::skip]
1100 btf_type_match!(match ty {
1101 BtfKind::Void => {
1102 "void"
1103 }
1104 BtfKind::Int => {
1105 "int"
1106 },
1107 BtfKind::Struct => "struct",
1108 _ => "default",
1109 });
1110 }
1111
1112 #[test]
1113 fn match_on_composite() {
1114 dummy_type!(ty);
1115 btf_type_match!(match ty {
1116 BtfKind::Composite(c) => c.is_struct,
1117 _ => false,
1118 });
1119 btf_type_match!(match ty {
1120 BtfKind::Composite(c) => {
1121 c.is_struct
1122 }
1123 _ => false,
1124 });
1125 #[rustfmt::skip]
1128 btf_type_match!(match ty {
1129 BtfKind::Composite(c) => {
1130 c.is_struct
1131 },
1132 _ => false,
1133 });
1134 }
1135
1136 #[test]
1137 fn match_arm_with_multiple_statements() {
1138 dummy_type!(ty);
1139
1140 btf_type_match!(match ty {
1141 BtfKind::Int(i) => {
1142 let _ = i;
1143 "int"
1144 }
1145 _ => {
1146 let _ = 1;
1147 "default"
1148 }
1149 });
1150 }
1151
1152 #[test]
1153 fn non_expression_guards() {
1154 dummy_type!(ty);
1155
1156 btf_type_match!(match ty {
1157 BtfKind::Int => {
1158 let _ = 1;
1159 "int"
1160 }
1161 BtfKind::Typedef | BtfKind::Const => {
1162 let _ = 1;
1163 "qualifier"
1164 }
1165 _ => {
1166 let _ = 1;
1167 "default"
1168 }
1169 });
1170
1171 btf_type_match!(match ty {
1172 BtfKind::Int => {
1173 let _ = 1;
1174 }
1175 BtfKind::Typedef | BtfKind::Const => {
1176 let _ = 1;
1177 }
1178 _ => {
1179 let _ = 1;
1180 }
1181 });
1182 }
1183
1184 #[test]
1185 fn linkage_type() {
1186 use std::mem::discriminant;
1187 use Linkage::*;
1188
1189 for t in [Static, Global, Extern, Unknown] {
1190 assert_eq!(discriminant(&t), discriminant(&Linkage::from(t as u32)));
1192 }
1193 }
1194}