the annoying macro:

from —— embassy-stm32/src/time_driver.rs

foreach_interrupt! {
    (TIM1, timer, $block:ident, CC, $irq:ident) => {
        #[cfg(time_driver_tim1)]
        #[cfg(feature = "rt")]
        #[interrupt]
        fn $irq() {
            DRIVER.on_interrupt()
        }
    };
    (TIM1, timer, $block:ident, CC, $irq:ident) => {
        #[cfg(time_driver_tim1)]
        #[cfg(feature = "rt")]
        #[interrupt]
        fn $irq() {
            DRIVER.on_interrupt()
        }
    };
    (TIM2, timer, $block:ident, CC, $irq:ident) => {
        #[cfg(time_driver_tim2)]
        #[cfg(feature = "rt")]
        #[interrupt]
        fn $irq() {
            DRIVER.on_interrupt()
        }
    };
    (TIM3, timer, $block:ident, CC, $irq:ident) => {
        #[cfg(time_driver_tim3)]
        #[cfg(feature = "rt")]
        #[interrupt]
        fn $irq() {
            DRIVER.on_interrupt()
        }
    };
    ......
}

Q1: why there are duplicate code?

to be written, still confused.

Q2: I want to know how this macro decoded

generate by embassy-stm32:

macro_rules! foreach_interrupt {
    ($($pat:tt => $code:tt;)*) => {
        macro_rules! __foreach_interrupt_inner {
            $(($pat) => $code;)*
            ($_:tt) => {}
        }
        __foreach_interrupt_inner!((ADC1,adc,ADC,GLOBAL,ADC));
        __foreach_interrupt_inner!((DMA1,dma,DMA,CH0,DMA1_STREAM0));
        __foreach_interrupt_inner!((DMA1,dma,DMA,CH1,DMA1_STREAM1));
        __foreach_interrupt_inner!((DMA1,dma,DMA,CH2,DMA1_STREAM2));
        __foreach_interrupt_inner!((DMA1,dma,DMA,CH3,DMA1_STREAM3));
        __foreach_interrupt_inner!((DMA1,dma,DMA,CH4,DMA1_STREAM4));
        __foreach_interrupt_inner!((DMA1,dma,DMA,CH5,DMA1_STREAM5));
        __foreach_interrupt_inner!((DMA1,dma,DMA,CH6,DMA1_STREAM6));
        __foreach_interrupt_inner!((DMA1,dma,DMA,CH7,DMA1_STREAM7));
        __foreach_interrupt_inner!((DMA2,dma,DMA,CH0,DMA2_STREAM0));
        .....
        __foreach_interrupt_inner!((SPI3,spi,SPI,GLOBAL,SPI3));
        __foreach_interrupt_inner!((TIM1,timer,TIM_ADV,BRK,TIM1_BRK_TIM9));
        __foreach_interrupt_inner!((TIM1,timer,TIM_ADV,CC,TIM1_CC));
        __foreach_interrupt_inner!((TIM1,timer,TIM_ADV,COM,TIM1_TRG_COM_TIM11));
        __foreach_interrupt_inner!((TIM1,timer,TIM_ADV,TRG,TIM1_TRG_COM_TIM11));
        __foreach_interrupt_inner!((TIM1,timer,TIM_ADV,UP,TIM1_UP_TIM10));
        __foreach_interrupt_inner!((TIM10,timer,TIM_1CH,BRK,TIM1_UP_TIM10));
        __foreach_interrupt_inner!((TIM10,timer,TIM_1CH,CC,TIM1_UP_TIM10));
        __foreach_interrupt_inner!((TIM10,timer,TIM_1CH,COM,TIM1_UP_TIM10));
        __foreach_interrupt_inner!((TIM10,timer,TIM_1CH,TRG,TIM1_UP_TIM10));
        __foreach_interrupt_inner!((TIM10,timer,TIM_1CH,UP,TIM1_UP_TIM10));
        __foreach_interrupt_inner!((TIM11,timer,TIM_1CH,BRK,TIM1_TRG_COM_TIM11));
        __foreach_interrupt_inner!((TIM11,timer,TIM_1CH,CC,TIM1_TRG_COM_TIM11));
        __foreach_interrupt_inner!((TIM11,timer,TIM_1CH,COM,TIM1_TRG_COM_TIM11));
        __foreach_interrupt_inner!((TIM11,timer,TIM_1CH,TRG,TIM1_TRG_COM_TIM11));
        __foreach_interrupt_inner!((TIM11,timer,TIM_1CH,UP,TIM1_TRG_COM_TIM11));
        __foreach_interrupt_inner!((TIM2,timer,TIM_GP32,BRK,TIM2));
        __foreach_interrupt_inner!((TIM2,timer,TIM_GP32,CC,TIM2));
        __foreach_interrupt_inner!((TIM2,timer,TIM_GP32,COM,TIM2));
        ....
    };
}

S2:

first, look at:

__foreach_interrupt_inner!((TIM1,timer,TIM_ADV,CC,TIM1_CC));

according to :

macro_rules! __foreach_interrupt_inner {
            $(($pat) => $code;)*
            ($_:tt) => {}
        }

and you need to be aware that the macro above is inside a macro. So you must notice $(($pat) => $code;)* . the outer$(…) comes from:

macro_rules! foreach_interrupt {
    ($($pat:tt => $code:tt;)*) => {
    }
	.....
}

so it will be expanded depending on the $($pat:tt => $code:tt;)* match.

for example, we will see :

(TIM1, timer, $block:ident, CC, $irq:ident) => {
        #[cfg(time_driver_tim1)]
        #[cfg(feature = "rt")]
        #[interrupt]
        fn $irq() {
            DRIVER.on_interrupt()
        }
    };
(TIM1, timer, $block:ident, CC, $irq:ident) => {
        #[cfg(time_driver_tim1)]
        #[cfg(feature = "rt")]
        #[interrupt]
        fn $irq() {
            DRIVER.on_interrupt()
        }
    };
(TIM2, timer, $block:ident, CC, $irq:ident) => {
        #[cfg(time_driver_tim2)]
        #[cfg(feature = "rt")]
        #[interrupt]
        fn $irq() {
            DRIVER.on_interrupt()
        }
    };
    ....

will be used to replace the pattern in __foreach_interrupt_inner as(the middle state, and I reserve the cfg):