<commit_before>use rustc_ast::ast;
use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_expand::base::{self, *};
use rustc_parse::parser::Parser;
use rustc_parse_format as parse;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{InnerSpan, Span};

struct AsmArgs {
    template: P<ast::Expr>,
    operands: Vec<(ast::InlineAsmOperand, Span)>,
    named_args: FxHashMap<Symbol, usize>,
    reg_args: FxHashSet<usize>,
    options: ast::InlineAsmOptions,
    options_span: Option<Span>,
}

fn parse_args<'a>(
    ecx: &mut ExtCtxt<'a>,
    sp: Span,
    tts: TokenStream,
) -> Result<AsmArgs, DiagnosticBuilder<'a>> {
    let mut p = ecx.new_parser_from_tts(tts);

    if p.token == token::Eof {
        return Err(ecx.struct_span_err(sp, "requires at least a template string argument"));
    }

    // Detect use of the legacy llvm_asm! syntax (which used to be called asm!)
    if p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) {
        let mut err =
            ecx.struct_span_err(sp, "the legacy LLVM-style asm! syntax is no longer supported");
        err.note("consider migrating to the new asm! syntax specified in RFC 2873");
        err.note("alternatively, switch to llvm_asm! to keep your code working as it is");

        // Find the span of the "asm!" so that we can offer an automatic suggestion
        let asm_span = sp.from_inner(InnerSpan::new(0, 4));
        if let Ok(s) = ecx.source_map().span_to_snippet(asm_span) {
            if s == "asm!" {
                err.span_suggestion(
                    asm_span,
                    "replace with",
                    "llvm_asm!".into(),
                    Applicability::MachineApplicable,
                );
            }
        }
        return Err(err);
    }

    let template = p.parse_expr()?;
    let mut args = AsmArgs {
        template,
        operands: vec![],
        named_args: FxHashMap::default(),
        reg_args: FxHashSet::default(),
        options: ast::InlineAsmOptions::empty(),
        options_span: None,
    };

    let mut first = true;
    while p.token != token::Eof {
        if !p.eat(&token::Comma) {
            if first {
                // After `asm!(""` we always expect *only* a comma...
                let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`");
                err.span_label(p.token.span, "expected `,`");
                p.maybe_annotate_with_ascription(&mut err, false);
                return Err(err);
            } else {
                // ...after that delegate to `expect` to also include the other expected tokens.
                return Err(p.expect(&token::Comma).err().unwrap());
            }
        }
        first = false;
        if p.token == token::Eof {
            break;
        } // accept trailing commas

        // Parse options
        if p.eat(&token::Ident(sym::options, false)) {
            parse_options(&mut p, &mut args)?;
            continue;
        }

        let span_start = p.token.span;

        // Parse operand names
        let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
            let (ident, _) = p.token.ident().unwrap();
            p.bump();
            p.expect(&token::Eq)?;
            Some(ident.name)
        } else {
            None
        };

        let mut explicit_reg = false;
        let op = if p.eat(&token::Ident(kw::In, false)) {
            let reg = parse_reg(&mut p, &mut explicit_reg)?;
            let expr = p.parse_expr()?;
            ast::InlineAsmOperand::In { reg, expr }
        } else if p.eat(&token::Ident(sym::out, false)) {
            let reg = parse_reg(&mut p, &mut explicit_reg)?;
            let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
            ast::InlineAsmOperand::Out { reg, expr, late: false }
        } else if p.eat(&token::Ident(sym::lateout, false)) {
            let reg = parse_reg(&mut p, &mut explicit_reg)?;
            let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
            ast::InlineAsmOperand::Out { reg, expr, late: true }
        } else if p.eat(&token::Ident(sym::inout, false)) {
            let reg = parse_reg(&mut p, &mut explicit_reg)?;
            let expr = p.parse_expr()?;
            if p.eat(&token::FatArrow) {
                let out_expr =
                    if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
                ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
            } else {
                ast::InlineAsmOperand::InOut { reg, expr, late: false }
            }
        } else if p.eat(&token::Ident(sym::inlateout, false)) {
            let reg = parse_reg(&mut p, &mut explicit_reg)?;
            let expr = p.parse_expr()?;
            if p.eat(&token::FatArrow) {
                let out_expr =
                    if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
                ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
            } else {
                ast::InlineAsmOperand::InOut { reg, expr, late: true }
            }
        } else if p.eat(&token::Ident(kw::Const, false)) {
            let expr = p.parse_expr()?;
            ast::InlineAsmOperand::Const { expr }
        } else {
            p.expect(&token::Ident(sym::sym, false))?;
            let expr = p.parse_expr()?;
            match expr.kind {
                ast::ExprKind::Path(..) => {}
                _ => {
                    let err = ecx
                        .struct_span_err(expr.span, "argument to `sym` must be a path expression");
                    return Err(err);
                }
            }
            ast::InlineAsmOperand::Sym { expr }
        };

        let span = span_start.to(p.prev_token.span);
        let slot = args.operands.len();
        args.operands.push((op, span));

        // Validate the order of named, positional & explicit register operands and options. We do
        // this at the end once we have the full span of the argument available.
        if let Some(options_span) = args.options_span {
            ecx.struct_span_err(span, "arguments are not allowed after options")
                .span_label(options_span, "previous options")
                .span_label(span, "argument")
                .emit();
        }
        if explicit_reg {
            if name.is_some() {
                ecx.struct_span_err(span, "explicit register arguments cannot have names").emit();
            }
            args.reg_args.insert(slot);
        } else if let Some(name) = name {
            if let Some(&prev) = args.named_args.get(&name) {
                ecx.struct_span_err(span, &format!("duplicate argument named `{}`", name))<commit_msg>asm: Unify pseudo-keyword parsing using `eat`, rather than a final `expect`<commit_after>use rustc_ast::ast;
use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_expand::base::{self, *};
use rustc_parse::parser::Parser;
use rustc_parse_format as parse;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{InnerSpan, Span};

struct AsmArgs {
    template: P<ast::Expr>,
    operands: Vec<(ast::InlineAsmOperand, Span)>,
    named_args: FxHashMap<Symbol, usize>,
    reg_args: FxHashSet<usize>,
    options: ast::InlineAsmOptions,
    options_span: Option<Span>,
}

fn parse_args<'a>(
    ecx: &mut ExtCtxt<'a>,
    sp: Span,
    tts: TokenStream,
) -> Result<AsmArgs, DiagnosticBuilder<'a>> {
    let mut p = ecx.new_parser_from_tts(tts);

    if p.token == token::Eof {
        return Err(ecx.struct_span_err(sp, "requires at least a template string argument"));
    }

    // Detect use of the legacy llvm_asm! syntax (which used to be called asm!)
    if p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) {
        let mut err =
            ecx.struct_span_err(sp, "the legacy LLVM-style asm! syntax is no longer supported");
        err.note("consider migrating to the new asm! syntax specified in RFC 2873");
        err.note("alternatively, switch to llvm_asm! to keep your code working as it is");

        // Find the span of the "asm!" so that we can offer an automatic suggestion
        let asm_span = sp.from_inner(InnerSpan::new(0, 4));
        if let Ok(s) = ecx.source_map().span_to_snippet(asm_span) {
            if s == "asm!" {
                err.span_suggestion(
                    asm_span,
                    "replace with",
                    "llvm_asm!".into(),
                    Applicability::MachineApplicable,
                );
            }
        }
        return Err(err);
    }

    let template = p.parse_expr()?;
    let mut args = AsmArgs {
        template,
        operands: vec![],
        named_args: FxHashMap::default(),
        reg_args: FxHashSet::default(),
        options: ast::InlineAsmOptions::empty(),
        options_span: None,
    };

    let mut first = true;
    while p.token != token::Eof {
        if !p.eat(&token::Comma) {
            if first {
                // After `asm!(""` we always expect *only* a comma...
                let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`");
                err.span_label(p.token.span, "expected `,`");
                p.maybe_annotate_with_ascription(&mut err, false);
                return Err(err);
            } else {
                // ...after that delegate to `expect` to also include the other expected tokens.
                return Err(p.expect(&token::Comma).err().unwrap());
            }
        }
        first = false;
        if p.token == token::Eof {
            break;
        } // accept trailing commas

        // Parse options
        if p.eat(&token::Ident(sym::options, false)) {
            parse_options(&mut p, &mut args)?;
            continue;
        }

        let span_start = p.token.span;

        // Parse operand names
        let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
            let (ident, _) = p.token.ident().unwrap();
            p.bump();
            p.expect(&token::Eq)?;
            Some(ident.name)
        } else {
            None
        };

        let mut explicit_reg = false;
        let op = if p.eat(&token::Ident(kw::In, false)) {
            let reg = parse_reg(&mut p, &mut explicit_reg)?;
            let expr = p.parse_expr()?;
            ast::InlineAsmOperand::In { reg, expr }
        } else if p.eat(&token::Ident(sym::out, false)) {
            let reg = parse_reg(&mut p, &mut explicit_reg)?;
            let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
            ast::InlineAsmOperand::Out { reg, expr, late: false }
        } else if p.eat(&token::Ident(sym::lateout, false)) {
            let reg = parse_reg(&mut p, &mut explicit_reg)?;
            let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
            ast::InlineAsmOperand::Out { reg, expr, late: true }
        } else if p.eat(&token::Ident(sym::inout, false)) {
            let reg = parse_reg(&mut p, &mut explicit_reg)?;
            let expr = p.parse_expr()?;
            if p.eat(&token::FatArrow) {
                let out_expr =
                    if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
                ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
            } else {
                ast::InlineAsmOperand::InOut { reg, expr, late: false }
            }
        } else if p.eat(&token::Ident(sym::inlateout, false)) {
            let reg = parse_reg(&mut p, &mut explicit_reg)?;
            let expr = p.parse_expr()?;
            if p.eat(&token::FatArrow) {
                let out_expr =
                    if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
                ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
            } else {
                ast::InlineAsmOperand::InOut { reg, expr, late: true }
            }
        } else if p.eat(&token::Ident(kw::Const, false)) {
            let expr = p.parse_expr()?;
            ast::InlineAsmOperand::Const { expr }
        } else if p.eat(&token::Ident(sym::sym, false)) {
            let expr = p.parse_expr()?;
            match expr.kind {
                ast::ExprKind::Path(..) => {}
                _ => {
                    let err = ecx
                        .struct_span_err(expr.span, "argument to `sym` must be a path expression");
                    return Err(err);
                }
            }
            ast::InlineAsmOperand::Sym { expr }
        } else {
            return Err(p.expect_one_of(&[], &[]).unwrap_err());
        };

        let span = span_start.to(p.prev_token.span);
        let slot = args.operands.len();
        args.operands.push((op, span));

        // Validate the order of named, positional & explicit register operands and options. We do
        // this at the end once we have the full span of the argument available.
        if let Some(options_span) = args.options_span {
            ecx.struct_span_err(span, "arguments are not allowed after options")
                .span_label(options_span, "previous options")
                .span_label(span, "argument")
                .emit();
        }
        if explicit_reg {
            if name.is_some() {
                ecx.struct_span_err(span, "explicit register arguments cannot have names").emit();
            }
            args.reg_args.insert(slot);
        } else if let Some(name) = name {
            if let Some(&prev) = args.named_args.get(&name) {
                ecx.struct_span_err(span, &format!("duplicate argument named `{}`", name))
{'fraction_non_alphanumeric': 0.13544258798435835, 'fraction_numerical': 0.0011375755421258443, 'mean_word_length': 1.9857779664614732, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3610239', 'n_tokens_mistral': 4326, 'n_tokens_neox': 4296, 'n_words': 1044}