about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFolkert de Vries <folkert@folkertdev.nl>2025-04-29 22:43:46 +0200
committerFolkert de Vries <folkert@folkertdev.nl>2025-05-18 11:28:19 +0200
commitde8e305ba8feae17e5bc1281647865dc3f1deadf (patch)
treeb32355328b11b283e261702567689888aca1e784
parent5af9652e5c733eb14c9a28b92c7a2608cbf7ea59 (diff)
downloadrust-de8e305ba8feae17e5bc1281647865dc3f1deadf.tar.gz
rust-de8e305ba8feae17e5bc1281647865dc3f1deadf.zip
a new parser generating the exact same error messages
Co-authored-by: Travis Cross <tc@traviscross.com>
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs376
1 files changed, 226 insertions, 150 deletions
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 3afa2d3dd8e..be6f6b60136 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -4,7 +4,7 @@ use rustc_ast::ptr::P;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::{AsmMacro, token};
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
-use rustc_errors::PResult;
+use rustc_errors::{DiagCtxtHandle, PResult};
 use rustc_expand::base::*;
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_parse::exp;
@@ -18,6 +18,21 @@ use {rustc_ast as ast, rustc_parse_format as parse};
 use crate::errors;
 use crate::util::{ExprToSpannedString, expr_to_spanned_string};
 
+/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise
+/// not validated at all.
+pub struct RawAsmArg {
+    pub kind: RawAsmArgKind,
+    pub span: Span,
+}
+
+pub enum RawAsmArgKind {
+    Template(P<ast::Expr>),
+    Operand(Option<Symbol>, ast::InlineAsmOperand),
+    Options(Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>),
+    ClobberAbi(Vec<(Symbol, Span)>),
+}
+
+/// Validated assembly arguments, ready for macro expansion.
 pub struct AsmArgs {
     pub templates: Vec<P<ast::Expr>>,
     pub operands: Vec<(ast::InlineAsmOperand, Span)>,
@@ -59,16 +74,6 @@ fn eat_operand_keyword<'a>(
     }
 }
 
-fn parse_args<'a>(
-    ecx: &ExtCtxt<'a>,
-    sp: Span,
-    tts: TokenStream,
-    asm_macro: AsmMacro,
-) -> PResult<'a, AsmArgs> {
-    let mut p = ecx.new_parser_from_tts(tts);
-    parse_asm_args(&mut p, sp, asm_macro)
-}
-
 fn parse_asm_operand<'a>(
     p: &mut Parser<'a>,
     asm_macro: AsmMacro,
@@ -139,31 +144,28 @@ fn parse_asm_operand<'a>(
     }))
 }
 
-// Primarily public for rustfmt consumption.
-// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm`
-pub fn parse_asm_args<'a>(
+// Public for rustfmt.
+pub fn parse_raw_asm_args<'a>(
     p: &mut Parser<'a>,
     sp: Span,
     asm_macro: AsmMacro,
-) -> PResult<'a, AsmArgs> {
+) -> PResult<'a, Vec<RawAsmArg>> {
     let dcx = p.dcx();
 
     if p.token == token::Eof {
         return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
     }
 
+    let mut args = Vec::new();
+
     let first_template = p.parse_expr()?;
-    let mut args = AsmArgs {
-        templates: vec![first_template],
-        operands: vec![],
-        named_args: Default::default(),
-        reg_args: Default::default(),
-        clobber_abis: Vec::new(),
-        options: ast::InlineAsmOptions::empty(),
-        options_spans: vec![],
-    };
+    args.push(RawAsmArg {
+        span: first_template.span,
+        kind: RawAsmArgKind::Template(first_template),
+    });
 
     let mut allow_templates = true;
+
     while p.token != token::Eof {
         if !p.eat(exp!(Comma)) {
             if allow_templates {
@@ -174,27 +176,39 @@ pub fn parse_asm_args<'a>(
                 return Err(p.expect(exp!(Comma)).err().unwrap());
             }
         }
+
+        // Accept trailing commas.
         if p.token == token::Eof {
             break;
-        } // accept trailing commas
+        }
+
+        let span_start = p.token.span;
 
-        // Parse clobber_abi
+        // Parse `clobber_abi`.
         if p.eat_keyword(exp!(ClobberAbi)) {
-            parse_clobber_abi(p, &mut args)?;
             allow_templates = false;
+
+            args.push(RawAsmArg {
+                kind: RawAsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
+                span: span_start.to(p.prev_token.span),
+            });
+
             continue;
         }
 
-        // Parse options
+        // Parse `options`.
         if p.eat_keyword(exp!(Options)) {
-            parse_options(p, &mut args, asm_macro)?;
             allow_templates = false;
+
+            args.push(RawAsmArg {
+                kind: RawAsmArgKind::Options(parse_options(p, asm_macro)?),
+                span: span_start.to(p.prev_token.span),
+            });
+
             continue;
         }
 
-        let span_start = p.token.span;
-
-        // Parse operand names
+        // 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();
@@ -205,60 +219,172 @@ pub fn parse_asm_args<'a>(
             None
         };
 
-        let Some(op) = parse_asm_operand(p, asm_macro)? else {
-            if allow_templates {
-                let template = p.parse_expr()?;
-                // If it can't possibly expand to a string, provide diagnostics here to include other
-                // things it could have been.
-                match template.kind {
-                    ast::ExprKind::Lit(token_lit)
-                        if matches!(
-                            token_lit.kind,
-                            token::LitKind::Str | token::LitKind::StrRaw(_)
-                        ) => {}
-                    ast::ExprKind::MacCall(..) => {}
-                    _ => {
-                        let err = dcx.create_err(errors::AsmExpectedOther {
-                            span: template.span,
-                            is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
-                        });
-                        return Err(err);
+        if let Some(op) = parse_asm_operand(p, asm_macro)? {
+            allow_templates = false;
+
+            args.push(RawAsmArg {
+                span: span_start.to(p.prev_token.span),
+                kind: RawAsmArgKind::Operand(name, op),
+            });
+        } else if allow_templates {
+            let template = p.parse_expr()?;
+            // If it can't possibly expand to a string, provide diagnostics here to include other
+            // things it could have been.
+            match template.kind {
+                ast::ExprKind::Lit(token_lit)
+                    if matches!(
+                        token_lit.kind,
+                        token::LitKind::Str | token::LitKind::StrRaw(_)
+                    ) => {}
+                ast::ExprKind::MacCall(..) => {}
+                _ => {
+                    let err = dcx.create_err(errors::AsmExpectedOther {
+                        span: template.span,
+                        is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
+                    });
+                    return Err(err);
+                }
+            }
+
+            args.push(RawAsmArg { span: template.span, kind: RawAsmArgKind::Template(template) });
+        } else {
+            p.unexpected_any()?
+        }
+    }
+
+    Ok(args)
+}
+
+fn parse_args<'a>(
+    ecx: &ExtCtxt<'a>,
+    sp: Span,
+    tts: TokenStream,
+    asm_macro: AsmMacro,
+) -> PResult<'a, AsmArgs> {
+    let mut p = ecx.new_parser_from_tts(tts);
+    parse_asm_args(&mut p, sp, asm_macro)
+}
+
+// public for use in rustfmt
+// FIXME: use `RawAsmArg` in the formatting code instead.
+pub fn parse_asm_args<'a>(
+    p: &mut Parser<'a>,
+    sp: Span,
+    asm_macro: AsmMacro,
+) -> PResult<'a, AsmArgs> {
+    let raw_args = parse_raw_asm_args(p, sp, asm_macro)?;
+    validate_raw_asm_args(p.dcx(), asm_macro, raw_args)
+}
+
+pub fn validate_raw_asm_args<'a>(
+    dcx: DiagCtxtHandle<'a>,
+    asm_macro: AsmMacro,
+    raw_args: Vec<RawAsmArg>,
+) -> PResult<'a, AsmArgs> {
+    let mut args = AsmArgs {
+        templates: vec![],
+        operands: vec![],
+        named_args: Default::default(),
+        reg_args: Default::default(),
+        clobber_abis: Vec::new(),
+        options: ast::InlineAsmOptions::empty(),
+        options_spans: vec![],
+    };
+
+    let mut allow_templates = true;
+
+    for arg in raw_args {
+        match arg.kind {
+            RawAsmArgKind::Template(template) => {
+                // The error for the first template is delayed.
+                if !allow_templates {
+                    match template.kind {
+                        ast::ExprKind::Lit(token_lit)
+                            if matches!(
+                                token_lit.kind,
+                                token::LitKind::Str | token::LitKind::StrRaw(_)
+                            ) => {}
+                        ast::ExprKind::MacCall(..) => {}
+                        _ => {
+                            let err = dcx.create_err(errors::AsmExpectedOther {
+                                span: template.span,
+                                is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
+                            });
+                            return Err(err);
+                        }
                     }
                 }
+
                 args.templates.push(template);
-                continue;
-            } else {
-                p.unexpected_any()?
             }
-        };
+            RawAsmArgKind::Operand(name, op) => {
+                allow_templates = false;
 
-        let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_)));
+                let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_)));
+                let span = arg.span;
+                let slot = args.operands.len();
+                args.operands.push((op, span));
 
-        allow_templates = false;
-        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
+                // clobber_abi/options. We do this at the end once we have the full span
+                // of the argument available.
 
-        // Validate the order of named, positional & explicit register operands and
-        // clobber_abi/options. We do this at the end once we have the full span
-        // of the argument available.
+                if explicit_reg {
+                    if name.is_some() {
+                        dcx.emit_err(errors::AsmExplicitRegisterName { span });
+                    }
+                    args.reg_args.insert(slot);
+                } else if let Some(name) = name {
+                    if let Some(&prev) = args.named_args.get(&name) {
+                        dcx.emit_err(errors::AsmDuplicateArg {
+                            span,
+                            name,
+                            prev: args.operands[prev].1,
+                        });
+                        continue;
+                    }
+                    args.named_args.insert(name, slot);
+                } else if !args.named_args.is_empty() || !args.reg_args.is_empty() {
+                    let named = args.named_args.values().map(|p| args.operands[*p].1).collect();
+                    let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect();
 
-        if explicit_reg {
-            if name.is_some() {
-                dcx.emit_err(errors::AsmExplicitRegisterName { span });
-            }
-            args.reg_args.insert(slot);
-        } else if let Some(name) = name {
-            if let Some(&prev) = args.named_args.get(&name) {
-                dcx.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 });
-                continue;
+                    dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
+                }
             }
-            args.named_args.insert(name, slot);
-        } else if !args.named_args.is_empty() || !args.reg_args.is_empty() {
-            let named = args.named_args.values().map(|p| args.operands[*p].1).collect();
-            let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect();
+            RawAsmArgKind::Options(new_options) => {
+                allow_templates = false;
+
+                for (symbol, option, span, full_span) in new_options {
+                    if !asm_macro.is_supported_option(option) {
+                        /*
+                        // Tool-only output
+                        p.dcx().emit_err(errors::AsmUnsupportedOption {
+                            span,
+                            symbol,
+                            full_span,
+                            macro_name: asm_macro.macro_name(),
+                        });
+                        */
+                    } else if args.options.contains(option) {
+                        // Tool-only output.
+                        dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
+                    } else {
+                        args.options |= option;
+                    }
+                }
 
-            dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
+                args.options_spans.push(arg.span);
+            }
+            RawAsmArgKind::ClobberAbi(new_abis) => {
+                allow_templates = false;
+
+                match &new_abis[..] {
+                    // This should have errored above during parsing.
+                    [] => unreachable!(),
+                    [(abi, _span)] => args.clobber_abis.push((*abi, arg.span)),
+                    _ => args.clobber_abis.extend(new_abis),
+                }
+            }
         }
     }
 
@@ -348,61 +474,14 @@ pub fn parse_asm_args<'a>(
     Ok(args)
 }
 
-/// Report a duplicate option error.
-///
-/// This function must be called immediately after the option token is parsed.
-/// Otherwise, the suggestion will be incorrect.
-fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
-    // Tool-only output
-    let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span };
-    p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
-}
-
-/// Report an invalid option error.
-///
-/// This function must be called immediately after the option token is parsed.
-/// Otherwise, the suggestion will be incorrect.
-fn err_unsupported_option(p: &Parser<'_>, asm_macro: AsmMacro, symbol: Symbol, span: Span) {
-    // Tool-only output
-    let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span };
-    p.dcx().emit_err(errors::AsmUnsupportedOption {
-        span,
-        symbol,
-        full_span,
-        macro_name: asm_macro.macro_name(),
-    });
-}
-
-/// Try to set the provided option in the provided `AsmArgs`.
-/// If it is already set, report a duplicate option error.
-///
-/// This function must be called immediately after the option token is parsed.
-/// Otherwise, the error will not point to the correct spot.
-fn try_set_option<'a>(
-    p: &Parser<'a>,
-    args: &mut AsmArgs,
-    asm_macro: AsmMacro,
-    symbol: Symbol,
-    option: ast::InlineAsmOptions,
-) {
-    if !asm_macro.is_supported_option(option) {
-        err_unsupported_option(p, asm_macro, symbol, p.prev_token.span);
-    } else if args.options.contains(option) {
-        err_duplicate_option(p, symbol, p.prev_token.span);
-    } else {
-        args.options |= option;
-    }
-}
-
 fn parse_options<'a>(
     p: &mut Parser<'a>,
-    args: &mut AsmArgs,
     asm_macro: AsmMacro,
-) -> PResult<'a, ()> {
-    let span_start = p.prev_token.span;
-
+) -> PResult<'a, Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>> {
     p.expect(exp!(OpenParen))?;
 
+    let mut options = Vec::new();
+
     while !p.eat(exp!(CloseParen)) {
         const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
             (exp!(Pure), ast::InlineAsmOptions::PURE),
@@ -418,6 +497,7 @@ fn parse_options<'a>(
 
         'blk: {
             for (exp, option) in OPTIONS {
+                // Gives a more accurate list of expected next tokens.
                 let kw_matched = if asm_macro.is_supported_option(option) {
                     p.eat_keyword(exp)
                 } else {
@@ -425,30 +505,39 @@ fn parse_options<'a>(
                 };
 
                 if kw_matched {
-                    try_set_option(p, args, asm_macro, exp.kw, option);
+                    let span = p.prev_token.span;
+                    let full_span =
+                        if p.token == token::Comma { span.to(p.token.span) } else { span };
+
+                    if !asm_macro.is_supported_option(option) {
+                        // Tool-only output.
+                        p.dcx().emit_err(errors::AsmUnsupportedOption {
+                            span,
+                            symbol: exp.kw,
+                            full_span,
+                            macro_name: asm_macro.macro_name(),
+                        });
+                    }
+
+                    options.push((exp.kw, option, span, full_span));
                     break 'blk;
                 }
             }
 
-            return p.unexpected();
+            return p.unexpected_any();
         }
 
-        // Allow trailing commas
+        // Allow trailing commas.
         if p.eat(exp!(CloseParen)) {
             break;
         }
         p.expect(exp!(Comma))?;
     }
 
-    let new_span = span_start.to(p.prev_token.span);
-    args.options_spans.push(new_span);
-
-    Ok(())
+    Ok(options)
 }
 
-fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> {
-    let span_start = p.prev_token.span;
-
+fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> {
     p.expect(exp!(OpenParen))?;
 
     if p.eat(exp!(CloseParen)) {
@@ -474,20 +563,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
         p.expect(exp!(Comma))?;
     }
 
-    let full_span = span_start.to(p.prev_token.span);
-
-    match &new_abis[..] {
-        // should have errored above during parsing
-        [] => unreachable!(),
-        [(abi, _span)] => args.clobber_abis.push((*abi, full_span)),
-        abis => {
-            for (abi, span) in abis {
-                args.clobber_abis.push((*abi, *span));
-            }
-        }
-    }
-
-    Ok(())
+    Ok(new_abis)
 }
 
 fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> {