From 1db4d607e7621a7d813743e83125859a47970f79 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 28 Oct 2019 00:29:23 +0100 Subject: [PATCH] parser: allow ABIs from literal macro fragments --- src/libsyntax/parse/parser.rs | 40 +++++++++++++------ src/libsyntax/parse/parser/expr.rs | 6 ++- src/libsyntax/parse/parser/item.rs | 2 +- src/libsyntax/parse/token.rs | 7 ---- src/test/ui/parser/bad-lit-suffixes.rs | 4 +- src/test/ui/parser/bad-lit-suffixes.stderr | 4 +- .../extern-abi-from-mac-literal-frag.rs | 26 ++++++++++++ 7 files changed, 63 insertions(+), 26 deletions(-) create mode 100644 src/test/ui/parser/extern-abi-from-mac-literal-frag.rs diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 382c1a517aa..0c358b1caaf 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1205,27 +1205,41 @@ fn recover_incorrect_vis_restriction(&mut self) -> PResult<'a, ()> { Ok(()) } - /// Parses `extern` followed by an optional ABI string, or nothing. + /// Parses `extern string_literal?`. + /// If `extern` is not found, the Rust ABI is used. + /// If `extern` is found and a `string_literal` does not follow, the C ABI is used. fn parse_extern_abi(&mut self) -> PResult<'a, Abi> { Ok(if self.eat_keyword(kw::Extern) { - let ext_sp = self.prev_span; - self.parse_opt_abi()?.unwrap_or_else(|| Abi::new(sym::C, ext_sp)) + self.parse_opt_abi()? } else { Abi::default() }) } - /// Parses a string as an ABI spec on an extern type or module. - fn parse_opt_abi(&mut self) -> PResult<'a, Option> { - match self.token.kind { - token::Literal(token::Lit { kind: token::Str, symbol, suffix }) | - token::Literal(token::Lit { kind: token::StrRaw(..), symbol, suffix }) => { - self.expect_no_suffix(self.token.span, "an ABI spec", suffix); - self.bump(); - Ok(Some(Abi::new(symbol, self.prev_span))) + /// Parses a string literal as an ABI spec. + /// If one is not found, the "C" ABI is used. + fn parse_opt_abi(&mut self) -> PResult<'a, Abi> { + let span = if self.token.can_begin_literal_or_bool() { + let ast::Lit { span, kind, .. } = self.parse_lit()?; + match kind { + ast::LitKind::Str(symbol, _) => return Ok(Abi::new(symbol, span)), + ast::LitKind::Err(_) => {} + _ => { + self.struct_span_err(span, "non-string ABI literal") + .span_suggestion( + span, + "specify the ABI with a string literal", + "\"C\"".to_string(), + Applicability::MaybeIncorrect, + ) + .emit(); + } } - _ => Ok(None), - } + span + } else { + self.prev_span + }; + Ok(Abi::new(sym::C, span)) } /// We are parsing `async fn`. If we are on Rust 2015, emit an error. diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs index 97b1092452a..80ea8f380fb 100644 --- a/src/libsyntax/parse/parser/expr.rs +++ b/src/libsyntax/parse/parser/expr.rs @@ -1116,7 +1116,11 @@ pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> { Err(self.span_fatal(token.span, &msg)) } Err(err) => { - let (lit, span) = (token.expect_lit(), token.span); + let span = token.span; + let lit = match token.kind { + token::Literal(lit) => lit, + _ => unreachable!(), + }; self.bump(); self.error_literal_from_token(err, lit, span); // Pack possible quotes and prefixes from the original literal into diff --git a/src/libsyntax/parse/parser/item.rs b/src/libsyntax/parse/parser/item.rs index 76411e7cf13..ebb1cf12996 100644 --- a/src/libsyntax/parse/parser/item.rs +++ b/src/libsyntax/parse/parser/item.rs @@ -110,7 +110,7 @@ fn parse_item_implementation( return Ok(Some(self.parse_item_extern_crate(lo, vis, attrs)?)); } - let abi = self.parse_opt_abi()?.unwrap_or_else(|| Abi::new(sym::C, extern_sp)); + let abi = self.parse_opt_abi()?; if self.eat_keyword(kw::Fn) { // EXTERN FUNCTION ITEM diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index ea82aad0eae..6f3da344ccf 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -402,13 +402,6 @@ pub fn is_lit(&self) -> bool { } } - crate fn expect_lit(&self) -> Lit { - match self.kind { - Literal(lit) => lit, - _ => panic!("`expect_lit` called on non-literal"), - } - } - /// Returns `true` if the token is any literal, a minus (which can prefix a literal, /// for example a '-42', or one of the boolean idents). pub fn can_begin_literal_or_bool(&self) -> bool { diff --git a/src/test/ui/parser/bad-lit-suffixes.rs b/src/test/ui/parser/bad-lit-suffixes.rs index 9f301db0995..7db83674efc 100644 --- a/src/test/ui/parser/bad-lit-suffixes.rs +++ b/src/test/ui/parser/bad-lit-suffixes.rs @@ -1,9 +1,9 @@ extern - "C"suffix //~ ERROR suffixes on an ABI spec are invalid + "C"suffix //~ ERROR suffixes on a string literal are invalid fn foo() {} extern - "C"suffix //~ ERROR suffixes on an ABI spec are invalid + "C"suffix //~ ERROR suffixes on a string literal are invalid {} fn main() { diff --git a/src/test/ui/parser/bad-lit-suffixes.stderr b/src/test/ui/parser/bad-lit-suffixes.stderr index 208fcf43d91..6b0049298ff 100644 --- a/src/test/ui/parser/bad-lit-suffixes.stderr +++ b/src/test/ui/parser/bad-lit-suffixes.stderr @@ -1,10 +1,10 @@ -error: suffixes on an ABI spec are invalid +error: suffixes on a string literal are invalid --> $DIR/bad-lit-suffixes.rs:2:5 | LL | "C"suffix | ^^^^^^^^^ invalid suffix `suffix` -error: suffixes on an ABI spec are invalid +error: suffixes on a string literal are invalid --> $DIR/bad-lit-suffixes.rs:6:5 | LL | "C"suffix diff --git a/src/test/ui/parser/extern-abi-from-mac-literal-frag.rs b/src/test/ui/parser/extern-abi-from-mac-literal-frag.rs new file mode 100644 index 00000000000..cb23f2c808c --- /dev/null +++ b/src/test/ui/parser/extern-abi-from-mac-literal-frag.rs @@ -0,0 +1,26 @@ +// check-pass + +// In this test we check that the parser accepts an ABI string when it +// comes from a macro `literal` fragment as opposed to a hardcoded string. + +fn main() {} + +macro_rules! abi_from_lit_frag { + ($abi:literal) => { + extern $abi { + fn _import(); + } + + extern $abi fn _export() {} + + type _PTR = extern $abi fn(); + } +} + +mod rust { + abi_from_lit_frag!("Rust"); +} + +mod c { + abi_from_lit_frag!("C"); +} -- GitLab