Skip to content

Commit a0adf53

Browse files
committed
Implement asm! in librustc_builtin_macros
1 parent 813a9fc commit a0adf53

File tree

7 files changed

+712
-120
lines changed

7 files changed

+712
-120
lines changed

src/libfmt_macros/lib.rs

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ use std::string;
2727

2828
use rustc_span::{InnerSpan, Symbol};
2929

30+
/// The type of format string that we are parsing.
31+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
32+
pub enum ParseMode {
33+
/// A normal format string as per `format_args!`.
34+
Format,
35+
/// An inline assembly template string for `asm!`.
36+
InlineAsm,
37+
}
38+
3039
#[derive(Copy, Clone)]
3140
struct InnerOffset(usize);
3241

@@ -163,6 +172,7 @@ pub struct ParseError {
163172
/// This is a recursive-descent parser for the sake of simplicity, and if
164173
/// necessary there's probably lots of room for improvement performance-wise.
165174
pub struct Parser<'a> {
175+
mode: ParseMode,
166176
input: &'a str,
167177
cur: iter::Peekable<str::CharIndices<'a>>,
168178
/// Error messages accumulated during parsing
@@ -179,6 +189,8 @@ pub struct Parser<'a> {
179189
last_opening_brace: Option<InnerSpan>,
180190
/// Whether the source string is comes from `println!` as opposed to `format!` or `print!`
181191
append_newline: bool,
192+
/// Whether this formatting string is a literal or it comes from a macro.
193+
is_literal: bool,
182194
}
183195

184196
impl<'a> Iterator for Parser<'a> {
@@ -201,7 +213,9 @@ impl<'a> Iterator for Parser<'a> {
201213
if let Some(end) = self.must_consume('}') {
202214
let start = self.to_span_index(pos);
203215
let end = self.to_span_index(end + 1);
204-
self.arg_places.push(start.to(end));
216+
if self.is_literal {
217+
self.arg_places.push(start.to(end));
218+
}
205219
}
206220
Some(NextArgument(arg))
207221
}
@@ -235,10 +249,13 @@ impl<'a> Parser<'a> {
235249
pub fn new(
236250
s: &'a str,
237251
style: Option<usize>,
238-
skips: Vec<usize>,
252+
snippet: Option<string::String>,
239253
append_newline: bool,
254+
mode: ParseMode,
240255
) -> Parser<'a> {
256+
let (skips, is_literal) = find_skips_from_snippet(snippet, style);
241257
Parser {
258+
mode,
242259
input: s,
243260
cur: s.char_indices().peekable(),
244261
errors: vec![],
@@ -248,6 +265,7 @@ impl<'a> Parser<'a> {
248265
skips,
249266
last_opening_brace: None,
250267
append_newline,
268+
is_literal,
251269
}
252270
}
253271

@@ -426,7 +444,10 @@ impl<'a> Parser<'a> {
426444
/// Parses an `Argument` structure, or what's contained within braces inside the format string.
427445
fn argument(&mut self) -> Argument<'a> {
428446
let pos = self.position();
429-
let format = self.format();
447+
let format = match self.mode {
448+
ParseMode::Format => self.format(),
449+
ParseMode::InlineAsm => self.inline_asm(),
450+
};
430451

431452
// Resolve position after parsing format spec.
432453
let pos = match pos {
@@ -574,6 +595,36 @@ impl<'a> Parser<'a> {
574595
spec
575596
}
576597

598+
/// Parses an inline assembly template modifier at the current position, returning the modifier
599+
/// in the `ty` field of the `FormatSpec` struct.
600+
fn inline_asm(&mut self) -> FormatSpec<'a> {
601+
let mut spec = FormatSpec {
602+
fill: None,
603+
align: AlignUnknown,
604+
flags: 0,
605+
precision: CountImplied,
606+
precision_span: None,
607+
width: CountImplied,
608+
width_span: None,
609+
ty: &self.input[..0],
610+
ty_span: None,
611+
};
612+
if !self.consume(':') {
613+
return spec;
614+
}
615+
616+
let ty_span_start = self.cur.peek().map(|(pos, _)| *pos);
617+
spec.ty = self.word();
618+
let ty_span_end = self.cur.peek().map(|(pos, _)| *pos);
619+
if !spec.ty.is_empty() {
620+
spec.ty_span = ty_span_start
621+
.and_then(|s| ty_span_end.map(|e| (s, e)))
622+
.map(|(start, end)| self.to_span_index(start).to(self.to_span_index(end)));
623+
}
624+
625+
spec
626+
}
627+
577628
/// Parses a `Count` parameter at the current position. This does not check
578629
/// for 'CountIsNextParam' because that is only used in precision, not
579630
/// width.
@@ -652,5 +703,103 @@ impl<'a> Parser<'a> {
652703
}
653704
}
654705

706+
/// Finds the indices of all characters that have been processed and differ between the actual
707+
/// written code (code snippet) and the `InternedString` that gets processed in the `Parser`
708+
/// in order to properly synthethise the intra-string `Span`s for error diagnostics.
709+
fn find_skips_from_snippet(
710+
snippet: Option<string::String>,
711+
str_style: Option<usize>,
712+
) -> (Vec<usize>, bool) {
713+
let snippet = match snippet {
714+
Some(ref s) if s.starts_with('"') || s.starts_with("r#") => s,
715+
_ => return (vec![], false),
716+
};
717+
718+
fn find_skips(snippet: &str, is_raw: bool) -> Vec<usize> {
719+
let mut eat_ws = false;
720+
let mut s = snippet.chars().enumerate().peekable();
721+
let mut skips = vec![];
722+
while let Some((pos, c)) = s.next() {
723+
match (c, s.peek()) {
724+
// skip whitespace and empty lines ending in '\\'
725+
('\\', Some((next_pos, '\n'))) if !is_raw => {
726+
eat_ws = true;
727+
skips.push(pos);
728+
skips.push(*next_pos);
729+
let _ = s.next();
730+
}
731+
('\\', Some((next_pos, '\n' | 'n' | 't'))) if eat_ws => {
732+
skips.push(pos);
733+
skips.push(*next_pos);
734+
let _ = s.next();
735+
}
736+
(' ' | '\n' | '\t', _) if eat_ws => {
737+
skips.push(pos);
738+
}
739+
('\\', Some((next_pos, 'n' | 't' | '0' | '\\' | '\'' | '\"'))) => {
740+
skips.push(*next_pos);
741+
let _ = s.next();
742+
}
743+
('\\', Some((_, 'x'))) if !is_raw => {
744+
for _ in 0..3 {
745+
// consume `\xAB` literal
746+
if let Some((pos, _)) = s.next() {
747+
skips.push(pos);
748+
} else {
749+
break;
750+
}
751+
}
752+
}
753+
('\\', Some((_, 'u'))) if !is_raw => {
754+
if let Some((pos, _)) = s.next() {
755+
skips.push(pos);
756+
}
757+
if let Some((next_pos, next_c)) = s.next() {
758+
if next_c == '{' {
759+
skips.push(next_pos);
760+
let mut i = 0; // consume up to 6 hexanumeric chars + closing `}`
761+
while let (Some((next_pos, c)), true) = (s.next(), i < 7) {
762+
if c.is_digit(16) {
763+
skips.push(next_pos);
764+
} else if c == '}' {
765+
skips.push(next_pos);
766+
break;
767+
} else {
768+
break;
769+
}
770+
i += 1;
771+
}
772+
} else if next_c.is_digit(16) {
773+
skips.push(next_pos);
774+
// We suggest adding `{` and `}` when appropriate, accept it here as if
775+
// it were correct
776+
let mut i = 0; // consume up to 6 hexanumeric chars
777+
while let (Some((next_pos, c)), _) = (s.next(), i < 6) {
778+
if c.is_digit(16) {
779+
skips.push(next_pos);
780+
} else {
781+
break;
782+
}
783+
i += 1;
784+
}
785+
}
786+
}
787+
}
788+
_ if eat_ws => {
789+
// `take_while(|c| c.is_whitespace())`
790+
eat_ws = false;
791+
}
792+
_ => {}
793+
}
794+
}
795+
skips
796+
}
797+
798+
let r_start = str_style.map(|r| r + 1).unwrap_or(0);
799+
let r_end = str_style.map(|r| r).unwrap_or(0);
800+
let s = &snippet[r_start + 1..snippet.len() - r_end - 1];
801+
(find_skips(s, str_style.is_some()), true)
802+
}
803+
655804
#[cfg(test)]
656805
mod tests;

src/libfmt_macros/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::*;
22

33
fn same(fmt: &'static str, p: &[Piece<'static>]) {
4-
let parser = Parser::new(fmt, None, vec![], false);
4+
let parser = Parser::new(fmt, None, vec![], false, ParseMode::Format);
55
assert_eq!(parser.collect::<Vec<Piece<'static>>>(), p);
66
}
77

@@ -20,7 +20,7 @@ fn fmtdflt() -> FormatSpec<'static> {
2020
}
2121

2222
fn musterr(s: &str) {
23-
let mut p = Parser::new(s, None, vec![], false);
23+
let mut p = Parser::new(s, None, vec![], false, ParseMode::Format);
2424
p.next();
2525
assert!(!p.errors.is_empty());
2626
}

0 commit comments

Comments
 (0)