Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,13 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
drop(self);
}

/// Cancels this diagnostic and returns its first message, if it exists.
pub fn cancel_into_message(self) -> Option<String> {
let s = self.diag.as_ref()?.messages.get(0)?.0.as_str().map(ToString::to_string);
self.cancel();
s
}

/// See `DiagCtxt::stash_diagnostic` for details.
pub fn stash(mut self, span: Span, key: StashKey) -> Option<ErrorGuaranteed> {
let diag = self.take_diag();
Expand Down
50 changes: 35 additions & 15 deletions compiler/rustc_expand/src/proc_macro_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult};
use rustc_parse::lexer::{StripTokens, nfc_normalize};
use rustc_parse::parser::Parser;
use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal};
use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream};
use rustc_proc_macro::bridge::{
DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, server,
};
Expand Down Expand Up @@ -483,35 +483,42 @@ impl server::FreeFunctions for Rustc<'_, '_> {
self.psess().file_depinfo.borrow_mut().insert(Symbol::intern(path));
}

fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, ()> {
fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, String> {
const ERROR_MSG: &str = "cannot parse string into literal";

let name = FileName::proc_macro_source_code(s);

let mut parser = unwrap_or_emit_fatal(new_parser_from_source_str(
self.psess(),
name,
s.to_owned(),
StripTokens::Nothing,
));
let mut parser =
new_parser_from_source_str(self.psess(), name, s.to_owned(), StripTokens::Nothing)
.map_err(|diags| {
let mut messages = diags.into_iter().map(Diag::cancel_into_message).flatten();
if let Some(msg) = messages.next() {
messages.for_each(drop);
format!("{ERROR_MSG}: {msg}")
} else {
ERROR_MSG.to_string()
}
})?;

let first_span = parser.token.span.data();
let minus_present = parser.eat(exp!(Minus));

let lit_span = parser.token.span.data();
let token::Literal(mut lit) = parser.token.kind else {
return Err(());
return Err("not a literal".to_string());
};

// Check no comment or whitespace surrounding the (possibly negative)
// literal, or more tokens after it.
if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() {
return Err(());
return Err("comment or whitespace around literal".to_string());
}

if minus_present {
// If minus is present, check no comment or whitespace in between it
// and the literal token.
if first_span.hi.0 != lit_span.lo.0 {
return Err(());
return Err("comment or whitespace after minus".to_string());
}

// Check literal is a kind we allow to be negated in a proc macro token.
Expand All @@ -525,7 +532,9 @@ impl server::FreeFunctions for Rustc<'_, '_> {
| token::LitKind::ByteStrRaw(_)
| token::LitKind::CStr
| token::LitKind::CStrRaw(_)
| token::LitKind::Err(_) => return Err(()),
| token::LitKind::Err(_) => {
return Err("non-numeric literal may not be negated".to_string());
}
token::LitKind::Integer | token::LitKind::Float => {}
}

Expand Down Expand Up @@ -562,13 +571,24 @@ impl server::TokenStream for Rustc<'_, '_> {
stream.is_empty()
}

fn from_str(&mut self, src: &str) -> Self::TokenStream {
unwrap_or_emit_fatal(source_str_to_stream(
fn from_str(&mut self, src: &str) -> Result<Self::TokenStream, String> {
const ERROR_MSG: &str = "cannot parse string into token stream";

source_str_to_stream(
self.psess(),
FileName::proc_macro_source_code(src),
src.to_string(),
Some(self.call_site),
))
)
.map_err(|diags| {
let mut messages = diags.into_iter().map(Diag::cancel_into_message).flatten();
if let Some(msg) = messages.next() {
messages.for_each(drop);
format!("{ERROR_MSG}: {msg}")
} else {
ERROR_MSG.to_string()
}
})
}

fn to_string(&mut self, stream: &Self::TokenStream) -> String {
Expand Down
4 changes: 2 additions & 2 deletions library/proc_macro/src/bridge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ macro_rules! with_api {
fn injected_env_var(var: &str) -> Option<String>;
fn track_env_var(var: &str, value: Option<&str>);
fn track_path(path: &str);
fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>;
fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, String>;
fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>);
},
TokenStream {
fn drop($self: $S::TokenStream);
fn clone($self: &$S::TokenStream) -> $S::TokenStream;
fn is_empty($self: &$S::TokenStream) -> bool;
fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
fn from_str(src: &str) -> $S::TokenStream;
fn from_str(src: &str) -> Result<$S::TokenStream, String>;
fn to_string($self: &$S::TokenStream) -> String;
fn from_token_tree(
tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>,
Expand Down
11 changes: 4 additions & 7 deletions library/proc_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,12 @@ impl !Sync for TokenStream {}
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
#[non_exhaustive]
#[derive(Debug)]
pub struct LexError;
pub struct LexError(String);

#[stable(feature = "proc_macro_lexerror_impls", since = "1.44.0")]
impl fmt::Display for LexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("cannot parse string into token stream")
f.write_str(&self.0)
}
}

Expand Down Expand Up @@ -194,7 +194,7 @@ impl FromStr for TokenStream {
type Err = LexError;

fn from_str(src: &str) -> Result<TokenStream, LexError> {
Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src))))
Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src).map_err(LexError)?)))
}
}

Expand Down Expand Up @@ -1559,10 +1559,7 @@ impl FromStr for Literal {
type Err = LexError;

fn from_str(src: &str) -> Result<Self, LexError> {
match bridge::client::FreeFunctions::literal_from_str(src) {
Ok(literal) => Ok(Literal(literal)),
Err(()) => Err(LexError),
}
bridge::client::FreeFunctions::literal_from_str(src).map(Literal).map_err(LexError)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,22 @@ impl server::FreeFunctions for RaSpanServer {
self.tracked_paths.insert(path.into());
}

#[cfg(bootstrap)]
fn literal_from_str(
&mut self,
s: &str,
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
literal_from_str(s, self.call_site)
}

#[cfg(not(bootstrap))]
fn literal_from_str(
&mut self,
s: &str,
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, String> {
literal_from_str(s, self.call_site).map_err(|()| "cannot parse string into literal".to_string())
}

fn emit_diagnostic(&mut self, _: bridge::Diagnostic<Self::Span>) {
// FIXME handle diagnostic
}
Expand All @@ -65,6 +74,12 @@ impl server::TokenStream for RaSpanServer {
fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
stream.is_empty()
}
#[cfg(not(bootstrap))]
fn from_str(&mut self, src: &str) -> Result<Self::TokenStream, String> {
Self::TokenStream::from_str(src, self.call_site)
.map_err(|e| format!("failed to parse str to token stream: {e}"))
}
#[cfg(bootstrap)]
fn from_str(&mut self, src: &str) -> Self::TokenStream {
Self::TokenStream::from_str(src, self.call_site).unwrap_or_else(|e| {
Self::TokenStream::from_str(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,35 @@ impl server::FreeFunctions for SpanIdServer {
}
fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {}
fn track_path(&mut self, _path: &str) {}
#[cfg(bootstrap)]
fn literal_from_str(
&mut self,
s: &str,
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
literal_from_str(s, self.call_site)
}

#[cfg(not(bootstrap))]
fn literal_from_str(
&mut self,
s: &str,
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, String> {
literal_from_str(s, self.call_site).map_err(|()| "cannot parse string into literal".to_string())
}

fn emit_diagnostic(&mut self, _: bridge::Diagnostic<Self::Span>) {}
}

impl server::TokenStream for SpanIdServer {
fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
stream.is_empty()
}
#[cfg(not(bootstrap))]
fn from_str(&mut self, src: &str) -> Result<Self::TokenStream, String> {
Self::TokenStream::from_str(src, self.call_site)
.map_err(|e| format!("failed to parse str to token stream: {e}"))
}
#[cfg(bootstrap)]
fn from_str(&mut self, src: &str) -> Self::TokenStream {
Self::TokenStream::from_str(src, self.call_site).unwrap_or_else(|e| {
Self::TokenStream::from_str(
Expand Down
6 changes: 5 additions & 1 deletion tests/ui/proc-macro/auxiliary/invalid-punct-ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,9 @@ pub fn invalid_raw_ident(_: TokenStream) -> TokenStream {

#[proc_macro]
pub fn lexer_failure(_: TokenStream) -> TokenStream {
"a b ) c".parse().expect("parsing failed without panic")
assert_eq!(
"a b ) c".parse::<TokenStream>().unwrap_err().to_string(),
"cannot parse string into token stream: unexpected closing delimiter: `)`"
);
TokenStream::new()
}
6 changes: 4 additions & 2 deletions tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ pub fn run() {
lit("3//\n4", NormalErr);
lit("18.u8E", NormalErr);
lit("/*a*/ //", NormalErr);
stream("1 ) 2", NormalErr);
stream("( x [ ) ]", NormalErr);
lit("1 ) 2", NormalErr);
lit("( x [ ) ]", NormalErr);
// FIXME: all of the cases below should return an Err and emit no diagnostics, but don't yet.

// emits diagnostics and returns LexError
Expand All @@ -122,8 +126,6 @@ pub fn run() {

for parse in [stream as fn(&str, Mode), lit] {
// emits diagnostic(s), then panics
parse("1 ) 2", OtherWithPanic);
parse("( x [ ) ]", OtherWithPanic);
parse("r#", OtherWithPanic);

// emits diagnostic(s), then returns Ok(Literal { kind: ErrWithGuar, .. })
Expand Down
8 changes: 2 additions & 6 deletions tests/ui/proc-macro/invalid-punct-ident-4.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
//@ proc-macro: invalid-punct-ident.rs
//@ needs-unwind proc macro panics to report errors
//@ check-pass

#[macro_use]
extern crate invalid_punct_ident;

lexer_failure!();
//~^ ERROR proc macro panicked
//~| ERROR unexpected closing delimiter: `)`

fn main() {
let _recovery_witness: () = 0; //~ ERROR mismatched types
}
fn main() {}
25 changes: 0 additions & 25 deletions tests/ui/proc-macro/invalid-punct-ident-4.stderr

This file was deleted.

37 changes: 1 addition & 36 deletions tests/ui/proc-macro/nonfatal-parsing.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,6 @@ help: consider inserting whitespace here
LL | c 'r'
| +

error: unexpected closing delimiter: `)`
--> $DIR/nonfatal-parsing.rs:15:5
|
LL | nonfatal_parsing::run!();
| ^^^^^^^^^^^^^^^^^^^^^^^^ unexpected closing delimiter
|
= note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info)

error: unexpected closing delimiter: `]`
--> $DIR/nonfatal-parsing.rs:15:5
|
LL | nonfatal_parsing::run!();
| -^^^^^^^^^^^^^^^^^^^^^^^
| |
| the nearest open delimiter
| missing open `(` for this delimiter
| unexpected closing delimiter
|
= note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info)

error: found invalid character; only `#` is allowed in raw string delimitation: \u{0}
--> $DIR/nonfatal-parsing.rs:15:5
|
Expand Down Expand Up @@ -117,21 +97,6 @@ LL | nonfatal_parsing::run!();
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
= note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info)

error: unexpected closing delimiter: `)`
--> <proc-macro source code>:1:3
|
LL | 1 ) 2
| ^ unexpected closing delimiter

error: unexpected closing delimiter: `]`
--> <proc-macro source code>:1:10
|
LL | ( x [ ) ]
| - - ^ unexpected closing delimiter
| | |
| | missing open `(` for this delimiter
| the nearest open delimiter

error: found invalid character; only `#` is allowed in raw string delimitation: \u{0}
--> <proc-macro source code>:1:1
|
Expand Down Expand Up @@ -192,6 +157,6 @@ error: invalid digit for a base 2 literal
LL | /*a*/ 0b2 //
| ^

error: aborting due to 22 previous errors
error: aborting due to 18 previous errors

For more information about this error, try `rustc --explain E0768`.
Loading
Loading