Skip to main content

rustc_builtin_macros/
asm.rs

1use rustc_ast as ast;
2use rustc_ast::tokenstream::TokenStream;
3use rustc_ast::{AsmMacro, token};
4use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
5use rustc_errors::PResult;
6use rustc_expand::base::*;
7use rustc_index::bit_set::GrowableBitSet;
8use rustc_parse::parser::asm::*;
9use rustc_parse_format as parse;
10use rustc_session::lint;
11use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym};
12use rustc_target::asm::InlineAsmArch;
13use smallvec::{SmallVec, smallvec};
14
15use crate::errors;
16use crate::util::{ExprToSpannedString, expr_to_spanned_string};
17
18/// Validated assembly arguments, ready for macro expansion.
19struct ValidatedAsmArgs {
20    pub templates: Vec<Box<ast::Expr>>,
21    pub operands: Vec<(ast::InlineAsmOperand, Span)>,
22    named_args: FxIndexMap<Symbol, usize>,
23    reg_args: GrowableBitSet<usize>,
24    pub clobber_abis: Vec<(Symbol, Span)>,
25    options: ast::InlineAsmOptions,
26    pub options_spans: Vec<Span>,
27}
28
29struct MacGlobalAsm {
30    item: ast::Item,
31}
32
33impl MacResult for MacGlobalAsm {
34    fn make_items(self: Box<Self>) -> Option<SmallVec<[Box<ast::Item>; 1]>> {
35        Some({
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(Box::new(self.item));
        vec
    } else {
        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [Box::new(self.item)])))
    }
}smallvec![Box::new(self.item)])
36    }
37
38    fn make_stmts(self: Box<Self>) -> Option<SmallVec<[ast::Stmt; 1]>> {
39        Some({
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(ast::Stmt {
                id: ast::DUMMY_NODE_ID,
                span: self.item.span,
                kind: ast::StmtKind::Item(Box::new(self.item)),
            });
        vec
    } else {
        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [ast::Stmt {
                                id: ast::DUMMY_NODE_ID,
                                span: self.item.span,
                                kind: ast::StmtKind::Item(Box::new(self.item)),
                            }])))
    }
}smallvec![ast::Stmt {
40            id: ast::DUMMY_NODE_ID,
41            span: self.item.span,
42            kind: ast::StmtKind::Item(Box::new(self.item)),
43        }])
44    }
45}
46
47fn parse_args<'a>(
48    ecx: &ExtCtxt<'a>,
49    sp: Span,
50    tts: TokenStream,
51    asm_macro: AsmMacro,
52) -> PResult<'a, ValidatedAsmArgs> {
53    let args = parse_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?;
54    validate_asm_args(ecx, asm_macro, args)
55}
56
57fn validate_asm_args<'a>(
58    ecx: &ExtCtxt<'a>,
59    asm_macro: AsmMacro,
60    args: Vec<AsmArg>,
61) -> PResult<'a, ValidatedAsmArgs> {
62    let dcx = ecx.dcx();
63
64    let strip_unconfigured = rustc_expand::config::StripUnconfigured {
65        sess: ecx.sess,
66        features: Some(ecx.ecfg.features),
67        config_tokens: false,
68        lint_node_id: ecx.current_expansion.lint_node_id,
69    };
70
71    let mut validated = ValidatedAsmArgs {
72        templates: ::alloc::vec::Vec::new()vec![],
73        operands: ::alloc::vec::Vec::new()vec![],
74        named_args: Default::default(),
75        reg_args: Default::default(),
76        clobber_abis: Vec::new(),
77        options: ast::InlineAsmOptions::empty(),
78        options_spans: ::alloc::vec::Vec::new()vec![],
79    };
80
81    let mut allow_templates = true;
82
83    for arg in args {
84        for attr in arg.attributes.0.iter() {
85            if !#[allow(non_exhaustive_omitted_patterns)] match attr.name() {
    Some(sym::cfg | sym::cfg_attr) => true,
    _ => false,
}matches!(attr.name(), Some(sym::cfg | sym::cfg_attr)) {
86                ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() });
87            }
88        }
89
90        // Skip arguments that are configured out.
91        if strip_unconfigured.configure(arg.attributes).is_none() {
92            continue;
93        }
94
95        match arg.kind {
96            AsmArgKind::Template(template) => {
97                // The error for the first template is delayed.
98                if !allow_templates {
99                    match template.kind {
100                        ast::ExprKind::Lit(token_lit)
101                            if #[allow(non_exhaustive_omitted_patterns)] match token_lit.kind {
    token::LitKind::Str | token::LitKind::StrRaw(_) => true,
    _ => false,
}matches!(
102                                token_lit.kind,
103                                token::LitKind::Str | token::LitKind::StrRaw(_)
104                            ) => {}
105                        ast::ExprKind::MacCall(..) => {}
106                        _ => {
107                            let err = dcx.create_err(errors::AsmExpectedOther {
108                                span: template.span,
109                                is_inline_asm: #[allow(non_exhaustive_omitted_patterns)] match asm_macro {
    AsmMacro::Asm => true,
    _ => false,
}matches!(asm_macro, AsmMacro::Asm),
110                            });
111                            return Err(err);
112                        }
113                    }
114                }
115
116                validated.templates.push(template);
117            }
118            AsmArgKind::Operand(name, op) => {
119                allow_templates = false;
120
121                let explicit_reg = #[allow(non_exhaustive_omitted_patterns)] match op.reg() {
    Some(ast::InlineAsmRegOrRegClass::Reg(_)) => true,
    _ => false,
}matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_)));
122                let span = arg.span;
123                let slot = validated.operands.len();
124                validated.operands.push((op, span));
125
126                // Validate the order of named, positional & explicit register operands and
127                // clobber_abi/options. We do this at the end once we have the full span
128                // of the argument available.
129
130                if explicit_reg {
131                    if name.is_some() {
132                        dcx.emit_err(errors::AsmExplicitRegisterName { span });
133                    }
134                    validated.reg_args.insert(slot);
135                } else if let Some(name) = name {
136                    if let Some(&prev) = validated.named_args.get(&name) {
137                        dcx.emit_err(errors::AsmDuplicateArg {
138                            span,
139                            name,
140                            prev: validated.operands[prev].1,
141                        });
142                        continue;
143                    }
144                    validated.named_args.insert(name, slot);
145                } else if !validated.named_args.is_empty() || !validated.reg_args.is_empty() {
146                    let named =
147                        validated.named_args.values().map(|p| validated.operands[*p].1).collect();
148                    let explicit =
149                        validated.reg_args.iter().map(|p| validated.operands[p].1).collect();
150
151                    dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
152                }
153            }
154            AsmArgKind::Options(new_options) => {
155                allow_templates = false;
156
157                for asm_option in new_options {
158                    let AsmOption { span, symbol, span_with_comma, options } = asm_option;
159
160                    if !asm_macro.is_supported_option(options) {
161                        // Tool-only output.
162                        dcx.emit_err(errors::AsmUnsupportedOption {
163                            span,
164                            symbol,
165                            span_with_comma,
166                            macro_name: asm_macro.macro_name(),
167                        });
168                    } else if validated.options.contains(options) {
169                        // Tool-only output.
170                        dcx.emit_err(errors::AsmOptAlreadyprovided {
171                            span,
172                            symbol,
173                            span_with_comma,
174                        });
175                    } else {
176                        validated.options |= asm_option.options;
177                    }
178                }
179
180                validated.options_spans.push(arg.span);
181            }
182            AsmArgKind::ClobberAbi(new_abis) => {
183                allow_templates = false;
184
185                match &new_abis[..] {
186                    // This should have errored above during parsing.
187                    [] => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
188                    [(abi, _span)] => validated.clobber_abis.push((*abi, arg.span)),
189                    _ => validated.clobber_abis.extend(new_abis),
190                }
191            }
192        }
193    }
194
195    if validated.options.contains(ast::InlineAsmOptions::NOMEM)
196        && validated.options.contains(ast::InlineAsmOptions::READONLY)
197    {
198        let spans = validated.options_spans.clone();
199        dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
200    }
201    if validated.options.contains(ast::InlineAsmOptions::PURE)
202        && validated.options.contains(ast::InlineAsmOptions::NORETURN)
203    {
204        let spans = validated.options_spans.clone();
205        dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
206    }
207    if validated.options.contains(ast::InlineAsmOptions::PURE)
208        && !validated
209            .options
210            .intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
211    {
212        let spans = validated.options_spans.clone();
213        dcx.emit_err(errors::AsmPureCombine { spans });
214    }
215
216    let mut have_real_output = false;
217    let mut outputs_sp = ::alloc::vec::Vec::new()vec![];
218    let mut regclass_outputs = ::alloc::vec::Vec::new()vec![];
219    let mut labels_sp = ::alloc::vec::Vec::new()vec![];
220    for (op, op_sp) in &validated.operands {
221        match op {
222            ast::InlineAsmOperand::Out { reg, expr, .. }
223            | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => {
224                outputs_sp.push(*op_sp);
225                have_real_output |= expr.is_some();
226                if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
227                    regclass_outputs.push(*op_sp);
228                }
229            }
230            ast::InlineAsmOperand::InOut { reg, .. } => {
231                outputs_sp.push(*op_sp);
232                have_real_output = true;
233                if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
234                    regclass_outputs.push(*op_sp);
235                }
236            }
237            ast::InlineAsmOperand::Label { .. } => {
238                labels_sp.push(*op_sp);
239            }
240            _ => {}
241        }
242    }
243    if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
244        dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() });
245    }
246    if validated.options.contains(ast::InlineAsmOptions::NORETURN)
247        && !outputs_sp.is_empty()
248        && labels_sp.is_empty()
249    {
250        let err = dcx.create_err(errors::AsmNoReturn { outputs_sp });
251        // Bail out now since this is likely to confuse MIR
252        return Err(err);
253    }
254    if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() {
255        dcx.emit_err(errors::AsmMayUnwind { labels_sp });
256    }
257
258    if !validated.clobber_abis.is_empty() {
259        match asm_macro {
260            AsmMacro::GlobalAsm | AsmMacro::NakedAsm => {
261                let err = dcx.create_err(errors::AsmUnsupportedClobberAbi {
262                    spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(),
263                    macro_name: asm_macro.macro_name(),
264                });
265
266                // Bail out now since this is likely to confuse later stages
267                return Err(err);
268            }
269            AsmMacro::Asm => {
270                if !regclass_outputs.is_empty() {
271                    dcx.emit_err(errors::AsmClobberNoReg {
272                        spans: regclass_outputs,
273                        clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(),
274                    });
275                }
276            }
277        }
278    }
279
280    Ok(validated)
281}
282
283fn expand_preparsed_asm(
284    ecx: &mut ExtCtxt<'_>,
285    asm_macro: AsmMacro,
286    args: ValidatedAsmArgs,
287) -> ExpandResult<Result<ast::InlineAsm, ErrorGuaranteed>, ()> {
288    let mut template = ::alloc::vec::Vec::new()vec![];
289    // Register operands are implicitly used since they are not allowed to be
290    // referenced in the template string.
291    let mut used = ::alloc::vec::from_elem(false, args.operands.len())vec![false; args.operands.len()];
292    for pos in args.reg_args.iter() {
293        used[pos] = true;
294    }
295    let named_pos: FxHashMap<usize, Symbol> =
296        args.named_args.iter().map(|(&sym, &idx)| (idx, sym)).collect();
297    let mut line_spans = Vec::with_capacity(args.templates.len());
298    let mut curarg = 0;
299
300    let mut template_strs = Vec::with_capacity(args.templates.len());
301
302    for (i, template_expr) in args.templates.into_iter().enumerate() {
303        if i != 0 {
304            template.push(ast::InlineAsmTemplatePiece::String("\n".into()));
305        }
306
307        let msg = "asm template must be a string literal";
308        let template_sp = template_expr.span;
309        let template_is_mac_call = #[allow(non_exhaustive_omitted_patterns)] match template_expr.kind {
    ast::ExprKind::MacCall(_) => true,
    _ => false,
}matches!(template_expr.kind, ast::ExprKind::MacCall(_));
310
311        // Gets the span inside `template_sp` corresponding to the given range
312        let span_in_template = |range: std::ops::Range<usize>| -> Span {
313            if template_is_mac_call {
314                // When the template is a macro call we can't reliably get inner spans
315                // so just use the entire template span (see ICEs #129503, #131292)
316                template_sp
317            } else {
318                template_sp.from_inner(InnerSpan::new(range.start, range.end))
319            }
320        };
321
322        let ExprToSpannedString {
323            symbol: template_str,
324            style: template_style,
325            span: template_span,
326            ..
327        } = {
328            let ExpandResult::Ready(mac) = expr_to_spanned_string(ecx, template_expr, msg) else {
329                return ExpandResult::Retry(());
330            };
331            match mac {
332                Ok(template_part) => template_part,
333                Err(err) => {
334                    return ExpandResult::Ready(Err(match err {
335                        Ok((err, _)) => err.emit(),
336                        Err(guar) => guar,
337                    }));
338                }
339            }
340        };
341
342        let str_style = match template_style {
343            ast::StrStyle::Cooked => None,
344            ast::StrStyle::Raw(raw) => Some(raw as usize),
345        };
346
347        let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok();
348        template_strs.push((
349            template_str,
350            template_snippet.as_deref().map(Symbol::intern),
351            template_sp,
352        ));
353        let template_str = template_str.as_str();
354
355        if let Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) = ecx.sess.asm_arch {
356            let find_span = |needle: &str| -> Span {
357                if let Some(snippet) = &template_snippet {
358                    if let Some(pos) = snippet.find(needle) {
359                        let end = pos
360                            + snippet[pos..]
361                                .find(|c| #[allow(non_exhaustive_omitted_patterns)] match c {
    '\n' | ';' | '\\' | '"' => true,
    _ => false,
}matches!(c, '\n' | ';' | '\\' | '"'))
362                                .unwrap_or(snippet[pos..].len() - 1);
363                        let inner = InnerSpan::new(pos, end);
364                        return template_sp.from_inner(inner);
365                    }
366                }
367                template_sp
368            };
369
370            if template_str.contains(".intel_syntax") {
371                ecx.psess().buffer_lint(
372                    lint::builtin::BAD_ASM_STYLE,
373                    find_span(".intel_syntax"),
374                    ecx.current_expansion.lint_node_id,
375                    errors::AvoidIntelSyntax,
376                );
377            }
378            if template_str.contains(".att_syntax") {
379                ecx.psess().buffer_lint(
380                    lint::builtin::BAD_ASM_STYLE,
381                    find_span(".att_syntax"),
382                    ecx.current_expansion.lint_node_id,
383                    errors::AvoidAttSyntax,
384                );
385            }
386        }
387
388        // Don't treat raw asm as a format string.
389        if args.options.contains(ast::InlineAsmOptions::RAW) {
390            template.push(ast::InlineAsmTemplatePiece::String(template_str.to_string().into()));
391            let template_num_lines = 1 + template_str.matches('\n').count();
392            line_spans.extend(std::iter::repeat_n(template_sp, template_num_lines));
393            continue;
394        }
395
396        let mut parser = parse::Parser::new(
397            template_str,
398            str_style,
399            template_snippet,
400            false,
401            parse::ParseMode::InlineAsm,
402        );
403        parser.curarg = curarg;
404
405        let mut unverified_pieces = Vec::new();
406        while let Some(piece) = parser.next() {
407            if !parser.errors.is_empty() {
408                break;
409            } else {
410                unverified_pieces.push(piece);
411            }
412        }
413
414        if !parser.errors.is_empty() {
415            let err = parser.errors.remove(0);
416
417            let err_sp = span_in_template(err.span);
418
419            let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("invalid asm template string: {0}",
                err.description))
    })format!("invalid asm template string: {}", err.description);
420            let mut e = ecx.dcx().struct_span_err(err_sp, msg);
421            e.span_label(err_sp, err.label + " in asm template string");
422            if let Some(note) = err.note {
423                e.note(note);
424            }
425            if let Some((label, span)) = err.secondary_label {
426                e.span_label(span_in_template(span), label);
427            }
428            let guar = e.emit();
429            return ExpandResult::Ready(Err(guar));
430        }
431
432        curarg = parser.curarg;
433
434        let mut arg_spans = parser
435            .arg_places
436            .iter()
437            .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end)));
438        for piece in unverified_pieces {
439            match piece {
440                parse::Piece::Lit(s) => {
441                    template.push(ast::InlineAsmTemplatePiece::String(s.to_string().into()))
442                }
443                parse::Piece::NextArgument(arg) => {
444                    let span = arg_spans.next().unwrap_or(template_sp);
445
446                    let operand_idx = match arg.position {
447                        parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => {
448                            if idx >= args.operands.len()
449                                || named_pos.contains_key(&idx)
450                                || args.reg_args.contains(idx)
451                            {
452                                let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("invalid reference to argument at index {0}",
                idx))
    })format!("invalid reference to argument at index {idx}");
453                                let mut err = ecx.dcx().struct_span_err(span, msg);
454                                err.span_label(span, "from here");
455
456                                let positional_args = args.operands.len()
457                                    - args.named_args.len()
458                                    - args.reg_args.len();
459                                let positional = if positional_args != args.operands.len() {
460                                    "positional "
461                                } else {
462                                    ""
463                                };
464                                let msg = match positional_args {
465                                    0 => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("no {0}arguments were given",
                positional))
    })format!("no {positional}arguments were given"),
466                                    1 => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("there is 1 {0}argument",
                positional))
    })format!("there is 1 {positional}argument"),
467                                    x => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("there are {0} {1}arguments", x,
                positional))
    })format!("there are {x} {positional}arguments"),
468                                };
469                                err.note(msg);
470
471                                if named_pos.contains_key(&idx) {
472                                    err.span_label(args.operands[idx].1, "named argument");
473                                    err.span_note(
474                                        args.operands[idx].1,
475                                        "named arguments cannot be referenced by position",
476                                    );
477                                } else if args.reg_args.contains(idx) {
478                                    err.span_label(
479                                        args.operands[idx].1,
480                                        "explicit register argument",
481                                    );
482                                    err.span_note(
483                                        args.operands[idx].1,
484                                        "explicit register arguments cannot be used in the asm template",
485                                    );
486                                    err.span_help(
487                                        args.operands[idx].1,
488                                        "use the register name directly in the assembly code",
489                                    );
490                                }
491                                err.emit();
492                                None
493                            } else {
494                                Some(idx)
495                            }
496                        }
497                        parse::ArgumentNamed(name) => {
498                            match args.named_args.get(&Symbol::intern(name)) {
499                                Some(&idx) => Some(idx),
500                                None => {
501                                    let span = arg.position_span;
502                                    ecx.dcx()
503                                        .create_err(errors::AsmNoMatchedArgumentName {
504                                            name: name.to_owned(),
505                                            span: span_in_template(span),
506                                        })
507                                        .emit();
508                                    None
509                                }
510                            }
511                        }
512                    };
513
514                    let mut chars = arg.format.ty.chars();
515                    let mut modifier = chars.next();
516                    if chars.next().is_some() {
517                        let span = arg.format.ty_span.map(span_in_template).unwrap_or(template_sp);
518                        ecx.dcx().emit_err(errors::AsmModifierInvalid { span });
519                        modifier = None;
520                    }
521
522                    if let Some(operand_idx) = operand_idx {
523                        used[operand_idx] = true;
524                        template.push(ast::InlineAsmTemplatePiece::Placeholder {
525                            operand_idx,
526                            modifier,
527                            span,
528                        });
529                    }
530                }
531            }
532        }
533
534        if parser.line_spans.is_empty() {
535            let template_num_lines = 1 + template_str.matches('\n').count();
536            line_spans.extend(std::iter::repeat_n(template_sp, template_num_lines));
537        } else {
538            line_spans.extend(
539                parser
540                    .line_spans
541                    .iter()
542                    .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))),
543            );
544        };
545    }
546
547    let mut unused_operands = ::alloc::vec::Vec::new()vec![];
548    let mut help_str = String::new();
549    for (idx, used) in used.into_iter().enumerate() {
550        if !used {
551            let msg = if let Some(sym) = named_pos.get(&idx) {
552                help_str.push_str(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(" {{{0}}}", sym))
    })format!(" {{{}}}", sym));
553                "named argument never used"
554            } else {
555                help_str.push_str(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(" {{{0}}}", idx))
    })format!(" {{{}}}", idx));
556                "argument never used"
557            };
558            unused_operands.push((args.operands[idx].1, msg));
559        }
560    }
561    match unused_operands[..] {
562        [] => {}
563        [(sp, msg)] => {
564            ecx.dcx()
565                .struct_span_err(sp, msg)
566                .with_span_label(sp, msg)
567                .with_help(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("if this argument is intentionally unused, consider using it in an asm comment: `\"/*{0} */\"`",
                help_str))
    })format!(
568                    "if this argument is intentionally unused, \
569                     consider using it in an asm comment: `\"/*{help_str} */\"`"
570                ))
571                .emit();
572        }
573        _ => {
574            let mut err = ecx.dcx().struct_span_err(
575                unused_operands.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(),
576                "multiple unused asm arguments",
577            );
578            for (sp, msg) in unused_operands {
579                err.span_label(sp, msg);
580            }
581            err.help(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("if these arguments are intentionally unused, consider using them in an asm comment: `\"/*{0} */\"`",
                help_str))
    })format!(
582                "if these arguments are intentionally unused, \
583                 consider using them in an asm comment: `\"/*{help_str} */\"`"
584            ));
585            err.emit();
586        }
587    }
588
589    ExpandResult::Ready(Ok(ast::InlineAsm {
590        asm_macro,
591        template,
592        template_strs: template_strs.into_boxed_slice(),
593        operands: args.operands,
594        clobber_abis: args.clobber_abis,
595        options: args.options,
596        line_spans,
597    }))
598}
599
600pub(super) fn expand_asm<'cx>(
601    ecx: &'cx mut ExtCtxt<'_>,
602    sp: Span,
603    tts: TokenStream,
604) -> MacroExpanderResult<'cx> {
605    ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::Asm) {
606        Ok(args) => {
607            let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::Asm, args) else {
608                return ExpandResult::Retry(());
609            };
610            let expr = match mac {
611                Ok(inline_asm) => Box::new(ast::Expr {
612                    id: ast::DUMMY_NODE_ID,
613                    kind: ast::ExprKind::InlineAsm(Box::new(inline_asm)),
614                    span: sp,
615                    attrs: ast::AttrVec::new(),
616                    tokens: None,
617                }),
618                Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
619            };
620            MacEager::expr(expr)
621        }
622        Err(err) => {
623            let guar = err.emit();
624            DummyResult::any(sp, guar)
625        }
626    })
627}
628
629pub(super) fn expand_naked_asm<'cx>(
630    ecx: &'cx mut ExtCtxt<'_>,
631    sp: Span,
632    tts: TokenStream,
633) -> MacroExpanderResult<'cx> {
634    ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) {
635        Ok(args) => {
636            let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::NakedAsm, args)
637            else {
638                return ExpandResult::Retry(());
639            };
640            let expr = match mac {
641                Ok(inline_asm) => Box::new(ast::Expr {
642                    id: ast::DUMMY_NODE_ID,
643                    kind: ast::ExprKind::InlineAsm(Box::new(inline_asm)),
644                    span: sp,
645                    attrs: ast::AttrVec::new(),
646                    tokens: None,
647                }),
648                Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
649            };
650            MacEager::expr(expr)
651        }
652        Err(err) => {
653            let guar = err.emit();
654            DummyResult::any(sp, guar)
655        }
656    })
657}
658
659pub(super) fn expand_global_asm<'cx>(
660    ecx: &'cx mut ExtCtxt<'_>,
661    sp: Span,
662    tts: TokenStream,
663) -> MacroExpanderResult<'cx> {
664    ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::GlobalAsm) {
665        Ok(args) => {
666            let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::GlobalAsm, args)
667            else {
668                return ExpandResult::Retry(());
669            };
670            match mac {
671                Ok(inline_asm) => Box::new(MacGlobalAsm {
672                    item: ast::Item {
673                        attrs: ast::AttrVec::new(),
674                        id: ast::DUMMY_NODE_ID,
675                        kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
676                        vis: ast::Visibility {
677                            span: sp.shrink_to_lo(),
678                            kind: ast::VisibilityKind::Inherited,
679                            tokens: None,
680                        },
681                        span: sp,
682                        tokens: None,
683                    },
684                }),
685                Err(guar) => DummyResult::any(sp, guar),
686            }
687        }
688        Err(err) => {
689            let guar = err.emit();
690            DummyResult::any(sp, guar)
691        }
692    })
693}