Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 89d138c

Browse files
committedMar 23, 2024
Auto merge of rust-lang#122959 - matthiaskrgr:rollup-f7cx6dd, r=matthiaskrgr
Rollup of 8 pull requests Successful merges: - rust-lang#116016 (Soft-destabilize `RustcEncodable` & `RustcDecodable`, remove from prelude in next edition) - rust-lang#121281 (regression test for rust-lang#103626) - rust-lang#122168 (Fix validation on substituted callee bodies in MIR inliner) - rust-lang#122217 (Handle str literals written with `'` lexed as lifetime) - rust-lang#122379 (transmute: caution against int2ptr transmutation) - rust-lang#122907 (Uniquify `ReError` on input mode in canonicalizer) - rust-lang#122942 (Add test in higher ranked subtype) - rust-lang#122943 (add a couple more ice tests) r? `@ghost` `@rustbot` modify labels: rollup
2 parents e50ab29 + 5f5362d commit 89d138c

File tree

65 files changed

+1700
-130
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1700
-130
lines changed
 

‎compiler/rustc_parse/src/errors.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -1987,6 +1987,17 @@ pub enum UnknownPrefixSugg {
19871987
style = "verbose"
19881988
)]
19891989
Whitespace(#[primary_span] Span),
1990+
#[multipart_suggestion(
1991+
parse_suggestion_str,
1992+
applicability = "maybe-incorrect",
1993+
style = "verbose"
1994+
)]
1995+
MeantStr {
1996+
#[suggestion_part(code = "\"")]
1997+
start: Span,
1998+
#[suggestion_part(code = "\"")]
1999+
end: Span,
2000+
},
19902001
}
19912002

19922003
#[derive(Diagnostic)]
@@ -2198,12 +2209,21 @@ pub enum MoreThanOneCharSugg {
21982209
ch: String,
21992210
},
22002211
#[suggestion(parse_use_double_quotes, code = "{sugg}", applicability = "machine-applicable")]
2201-
Quotes {
2212+
QuotesFull {
22022213
#[primary_span]
22032214
span: Span,
22042215
is_byte: bool,
22052216
sugg: String,
22062217
},
2218+
#[multipart_suggestion(parse_use_double_quotes, applicability = "machine-applicable")]
2219+
Quotes {
2220+
#[suggestion_part(code = "{prefix}\"")]
2221+
start: Span,
2222+
#[suggestion_part(code = "\"")]
2223+
end: Span,
2224+
is_byte: bool,
2225+
prefix: &'static str,
2226+
},
22072227
}
22082228

22092229
#[derive(Subdiagnostic)]

‎compiler/rustc_parse/src/lexer/mod.rs

+52-5
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ pub(crate) fn parse_token_trees<'psess, 'src>(
6363
cursor,
6464
override_span,
6565
nbsp_is_whitespace: false,
66+
last_lifetime: None,
6667
};
6768
let (stream, res, unmatched_delims) =
6869
tokentrees::TokenTreesReader::parse_all_token_trees(string_reader);
@@ -105,6 +106,10 @@ struct StringReader<'psess, 'src> {
105106
/// in this file, it's safe to treat further occurrences of the non-breaking
106107
/// space character as whitespace.
107108
nbsp_is_whitespace: bool,
109+
110+
/// Track the `Span` for the leading `'` of the last lifetime. Used for
111+
/// diagnostics to detect possible typo where `"` was meant.
112+
last_lifetime: Option<Span>,
108113
}
109114

110115
impl<'psess, 'src> StringReader<'psess, 'src> {
@@ -130,6 +135,18 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
130135

131136
debug!("next_token: {:?}({:?})", token.kind, self.str_from(start));
132137

138+
if let rustc_lexer::TokenKind::Semi
139+
| rustc_lexer::TokenKind::LineComment { .. }
140+
| rustc_lexer::TokenKind::BlockComment { .. }
141+
| rustc_lexer::TokenKind::CloseParen
142+
| rustc_lexer::TokenKind::CloseBrace
143+
| rustc_lexer::TokenKind::CloseBracket = token.kind
144+
{
145+
// Heuristic: we assume that it is unlikely we're dealing with an unterminated
146+
// string surrounded by single quotes.
147+
self.last_lifetime = None;
148+
}
149+
133150
// Now "cook" the token, converting the simple `rustc_lexer::TokenKind` enum into a
134151
// rich `rustc_ast::TokenKind`. This turns strings into interned symbols and runs
135152
// additional validation.
@@ -247,6 +264,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
247264
// expansion purposes. See #12512 for the gory details of why
248265
// this is necessary.
249266
let lifetime_name = self.str_from(start);
267+
self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));
250268
if starts_with_number {
251269
let span = self.mk_sp(start, self.pos);
252270
self.dcx().struct_err("lifetimes cannot start with a number")
@@ -395,10 +413,21 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
395413
match kind {
396414
rustc_lexer::LiteralKind::Char { terminated } => {
397415
if !terminated {
398-
self.dcx()
416+
let mut err = self
417+
.dcx()
399418
.struct_span_fatal(self.mk_sp(start, end), "unterminated character literal")
400-
.with_code(E0762)
401-
.emit()
419+
.with_code(E0762);
420+
if let Some(lt_sp) = self.last_lifetime {
421+
err.multipart_suggestion(
422+
"if you meant to write a string literal, use double quotes",
423+
vec![
424+
(lt_sp, "\"".to_string()),
425+
(self.mk_sp(start, start + BytePos(1)), "\"".to_string()),
426+
],
427+
Applicability::MaybeIncorrect,
428+
);
429+
}
430+
err.emit()
402431
}
403432
self.cook_unicode(token::Char, Mode::Char, start, end, 1, 1) // ' '
404433
}
@@ -669,15 +698,33 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
669698
let expn_data = prefix_span.ctxt().outer_expn_data();
670699

671700
if expn_data.edition >= Edition::Edition2021 {
701+
let mut silence = false;
672702
// In Rust 2021, this is a hard error.
673703
let sugg = if prefix == "rb" {
674704
Some(errors::UnknownPrefixSugg::UseBr(prefix_span))
675705
} else if expn_data.is_root() {
676-
Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi()))
706+
if self.cursor.first() == '\''
707+
&& let Some(start) = self.last_lifetime
708+
&& self.cursor.third() != '\''
709+
{
710+
// An "unclosed `char`" error will be emitted already, silence redundant error.
711+
silence = true;
712+
Some(errors::UnknownPrefixSugg::MeantStr {
713+
start,
714+
end: self.mk_sp(self.pos, self.pos + BytePos(1)),
715+
})
716+
} else {
717+
Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi()))
718+
}
677719
} else {
678720
None
679721
};
680-
self.dcx().emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg });
722+
let err = errors::UnknownPrefix { span: prefix_span, prefix, sugg };
723+
if silence {
724+
self.dcx().create_err(err).delay_as_bug();
725+
} else {
726+
self.dcx().emit_err(err);
727+
}
681728
} else {
682729
// Before Rust 2021, only emit a lint for migration.
683730
self.psess.buffer_lint_with_diagnostic(

‎compiler/rustc_parse/src/lexer/unescape_error_reporting.rs

+15-5
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,21 @@ pub(crate) fn emit_unescape_error(
9595
}
9696
escaped.push(c);
9797
}
98-
let sugg = format!("{prefix}\"{escaped}\"");
99-
MoreThanOneCharSugg::Quotes {
100-
span: full_lit_span,
101-
is_byte: mode == Mode::Byte,
102-
sugg,
98+
if escaped.len() != lit.len() || full_lit_span.is_empty() {
99+
let sugg = format!("{prefix}\"{escaped}\"");
100+
MoreThanOneCharSugg::QuotesFull {
101+
span: full_lit_span,
102+
is_byte: mode == Mode::Byte,
103+
sugg,
104+
}
105+
} else {
106+
MoreThanOneCharSugg::Quotes {
107+
start: full_lit_span
108+
.with_hi(full_lit_span.lo() + BytePos((prefix.len() + 1) as u32)),
109+
end: full_lit_span.with_lo(full_lit_span.hi() - BytePos(1)),
110+
is_byte: mode == Mode::Byte,
111+
prefix,
112+
}
103113
}
104114
});
105115
dcx.emit_err(UnescapeError::MoreThanOneChar {
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.