Skip to content

Commit faf91ab

Browse files
committed
reduce the amount of panics in {TokenStream, Literal}::from_str calls
1 parent 66bc5a4 commit faf91ab

File tree

12 files changed

+104
-104
lines changed

12 files changed

+104
-104
lines changed

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,13 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
14241424
drop(self);
14251425
}
14261426

1427+
/// Cancels this diagnostic and returns its first message, if it exists.
1428+
pub fn cancel_into_message(self) -> Option<String> {
1429+
let s = self.diag.as_ref()?.messages.get(0)?.0.as_str().map(ToString::to_string);
1430+
self.cancel();
1431+
s
1432+
}
1433+
14271434
/// See `DiagCtxt::stash_diagnostic` for details.
14281435
pub fn stash(mut self, span: Span, key: StashKey) -> Option<ErrorGuaranteed> {
14291436
let diag = self.take_diag();

compiler/rustc_expand/src/proc_macro_server.rs

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxHashMap;
1010
use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult};
1111
use rustc_parse::lexer::{StripTokens, nfc_normalize};
1212
use rustc_parse::parser::Parser;
13-
use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal};
13+
use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream};
1414
use rustc_proc_macro::bridge::{
1515
DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, server,
1616
};
@@ -483,35 +483,42 @@ impl server::FreeFunctions for Rustc<'_, '_> {
483483
self.psess().file_depinfo.borrow_mut().insert(Symbol::intern(path));
484484
}
485485

486-
fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, ()> {
486+
fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, String> {
487+
const ERROR_MSG: &str = "cannot parse string into literal";
488+
487489
let name = FileName::proc_macro_source_code(s);
488490

489-
let mut parser = unwrap_or_emit_fatal(new_parser_from_source_str(
490-
self.psess(),
491-
name,
492-
s.to_owned(),
493-
StripTokens::Nothing,
494-
));
491+
let mut parser =
492+
new_parser_from_source_str(self.psess(), name, s.to_owned(), StripTokens::Nothing)
493+
.map_err(|diags| {
494+
let mut messages = diags.into_iter().map(Diag::cancel_into_message).flatten();
495+
if let Some(msg) = messages.next() {
496+
messages.for_each(drop);
497+
format!("{ERROR_MSG}: {msg}")
498+
} else {
499+
ERROR_MSG.to_string()
500+
}
501+
})?;
495502

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

499506
let lit_span = parser.token.span.data();
500507
let token::Literal(mut lit) = parser.token.kind else {
501-
return Err(());
508+
return Err("not a literal".to_string());
502509
};
503510

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

510517
if minus_present {
511518
// If minus is present, check no comment or whitespace in between it
512519
// and the literal token.
513520
if first_span.hi.0 != lit_span.lo.0 {
514-
return Err(());
521+
return Err("comment or whitespace after minus".to_string());
515522
}
516523

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

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

565-
fn from_str(&mut self, src: &str) -> Self::TokenStream {
566-
unwrap_or_emit_fatal(source_str_to_stream(
574+
fn from_str(&mut self, src: &str) -> Result<Self::TokenStream, String> {
575+
const ERROR_MSG: &str = "cannot parse string into token stream";
576+
577+
source_str_to_stream(
567578
self.psess(),
568579
FileName::proc_macro_source_code(src),
569580
src.to_string(),
570581
Some(self.call_site),
571-
))
582+
)
583+
.map_err(|diags| {
584+
let mut messages = diags.into_iter().map(Diag::cancel_into_message).flatten();
585+
if let Some(msg) = messages.next() {
586+
messages.for_each(drop);
587+
format!("{ERROR_MSG}: {msg}")
588+
} else {
589+
ERROR_MSG.to_string()
590+
}
591+
})
572592
}
573593

574594
fn to_string(&mut self, stream: &Self::TokenStream) -> String {

library/proc_macro/src/bridge/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ macro_rules! with_api {
5353
fn injected_env_var(var: &str) -> Option<String>;
5454
fn track_env_var(var: &str, value: Option<&str>);
5555
fn track_path(path: &str);
56-
fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>;
56+
fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, String>;
5757
fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>);
5858
},
5959
TokenStream {
6060
fn drop($self: $S::TokenStream);
6161
fn clone($self: &$S::TokenStream) -> $S::TokenStream;
6262
fn is_empty($self: &$S::TokenStream) -> bool;
6363
fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
64-
fn from_str(src: &str) -> $S::TokenStream;
64+
fn from_str(src: &str) -> Result<$S::TokenStream, String>;
6565
fn to_string($self: &$S::TokenStream) -> String;
6666
fn from_token_tree(
6767
tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>,

library/proc_macro/src/lib.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,12 @@ impl !Sync for TokenStream {}
110110
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
111111
#[non_exhaustive]
112112
#[derive(Debug)]
113-
pub struct LexError;
113+
pub struct LexError(String);
114114

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

@@ -194,7 +194,7 @@ impl FromStr for TokenStream {
194194
type Err = LexError;
195195

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

@@ -1559,10 +1559,7 @@ impl FromStr for Literal {
15591559
type Err = LexError;
15601560

15611561
fn from_str(src: &str) -> Result<Self, LexError> {
1562-
match bridge::client::FreeFunctions::literal_from_str(src) {
1563-
Ok(literal) => Ok(Literal(literal)),
1564-
Err(()) => Err(LexError),
1565-
}
1562+
bridge::client::FreeFunctions::literal_from_str(src).map(Literal).map_err(LexError)
15661563
}
15671564
}
15681565

src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,22 @@ impl server::FreeFunctions for RaSpanServer {
4949
self.tracked_paths.insert(path.into());
5050
}
5151

52+
#[cfg(bootstrap)]
5253
fn literal_from_str(
5354
&mut self,
5455
s: &str,
5556
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
5657
literal_from_str(s, self.call_site)
5758
}
5859

60+
#[cfg(not(bootstrap))]
61+
fn literal_from_str(
62+
&mut self,
63+
s: &str,
64+
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, String> {
65+
literal_from_str(s, self.call_site).map_err(|()| "cannot parse string into literal".to_string())
66+
}
67+
5968
fn emit_diagnostic(&mut self, _: bridge::Diagnostic<Self::Span>) {
6069
// FIXME handle diagnostic
6170
}
@@ -65,6 +74,12 @@ impl server::TokenStream for RaSpanServer {
6574
fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
6675
stream.is_empty()
6776
}
77+
#[cfg(not(bootstrap))]
78+
fn from_str(&mut self, src: &str) -> Result<Self::TokenStream, String> {
79+
Self::TokenStream::from_str(src, self.call_site)
80+
.map_err(|e| format!("failed to parse str to token stream: {e}"))
81+
}
82+
#[cfg(bootstrap)]
6883
fn from_str(&mut self, src: &str) -> Self::TokenStream {
6984
Self::TokenStream::from_str(src, self.call_site).unwrap_or_else(|e| {
7085
Self::TokenStream::from_str(

src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,35 @@ impl server::FreeFunctions for SpanIdServer {
4040
}
4141
fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {}
4242
fn track_path(&mut self, _path: &str) {}
43+
#[cfg(bootstrap)]
4344
fn literal_from_str(
4445
&mut self,
4546
s: &str,
4647
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
4748
literal_from_str(s, self.call_site)
4849
}
4950

51+
#[cfg(not(bootstrap))]
52+
fn literal_from_str(
53+
&mut self,
54+
s: &str,
55+
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, String> {
56+
literal_from_str(s, self.call_site).map_err(|()| "cannot parse string into literal".to_string())
57+
}
58+
5059
fn emit_diagnostic(&mut self, _: bridge::Diagnostic<Self::Span>) {}
5160
}
5261

5362
impl server::TokenStream for SpanIdServer {
5463
fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
5564
stream.is_empty()
5665
}
66+
#[cfg(not(bootstrap))]
67+
fn from_str(&mut self, src: &str) -> Result<Self::TokenStream, String> {
68+
Self::TokenStream::from_str(src, self.call_site)
69+
.map_err(|e| format!("failed to parse str to token stream: {e}"))
70+
}
71+
#[cfg(bootstrap)]
5772
fn from_str(&mut self, src: &str) -> Self::TokenStream {
5873
Self::TokenStream::from_str(src, self.call_site).unwrap_or_else(|e| {
5974
Self::TokenStream::from_str(

tests/ui/proc-macro/auxiliary/invalid-punct-ident.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,9 @@ pub fn invalid_raw_ident(_: TokenStream) -> TokenStream {
2020

2121
#[proc_macro]
2222
pub fn lexer_failure(_: TokenStream) -> TokenStream {
23-
"a b ) c".parse().expect("parsing failed without panic")
23+
assert_eq!(
24+
"a b ) c".parse::<TokenStream>().unwrap_err().to_string(),
25+
"cannot parse string into token stream: unexpected closing delimiter: `)`"
26+
);
27+
TokenStream::new()
2428
}

tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ pub fn run() {
110110
lit("3//\n4", NormalErr);
111111
lit("18.u8E", NormalErr);
112112
lit("/*a*/ //", NormalErr);
113+
stream("1 ) 2", NormalErr);
114+
stream("( x [ ) ]", NormalErr);
115+
lit("1 ) 2", NormalErr);
116+
lit("( x [ ) ]", NormalErr);
113117
// FIXME: all of the cases below should return an Err and emit no diagnostics, but don't yet.
114118

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

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

129131
// emits diagnostic(s), then returns Ok(Literal { kind: ErrWithGuar, .. })
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
//@ proc-macro: invalid-punct-ident.rs
2-
//@ needs-unwind proc macro panics to report errors
2+
//@ check-pass
33

44
#[macro_use]
55
extern crate invalid_punct_ident;
66

77
lexer_failure!();
8-
//~^ ERROR proc macro panicked
9-
//~| ERROR unexpected closing delimiter: `)`
108

11-
fn main() {
12-
let _recovery_witness: () = 0; //~ ERROR mismatched types
13-
}
9+
fn main() {}

tests/ui/proc-macro/invalid-punct-ident-4.stderr

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)