diff options
| author | bors <bors@rust-lang.org> | 2024-03-24 02:30:17 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-03-24 02:30:17 +0000 |
| commit | 548e14b43963882fb758deb89e8258d9b8c2fc2a (patch) | |
| tree | ff8336cc01e7d2939240a37acba7096caeb19377 /compiler | |
| parent | 9b8d12cf4c2311203aea83315552b15993bd4f81 (diff) | |
| parent | 6f16b41a4b299bfa31355e2d05110f54df93f9d8 (diff) | |
| download | rust-548e14b43963882fb758deb89e8258d9b8c2fc2a.tar.gz rust-548e14b43963882fb758deb89e8258d9b8c2fc2a.zip | |
Auto merge of #122966 - matthiaskrgr:rollup-20k8nsm, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #121281 (regression test for #103626) - #122168 (Fix validation on substituted callee bodies in MIR inliner) - #122217 (Handle str literals written with `'` lexed as lifetime) - #122379 (transmute: caution against int2ptr transmutation) - #122840 (`rustdoc --test`: Prevent reaching the maximum size of command-line by using files for arguments if there are too many) - #122907 (Uniquify `ReError` on input mode in canonicalizer) - #122942 (Add test in higher ranked subtype) - #122943 (add a couple more ice tests) - #122963 (core/panicking: fix outdated comment) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_const_eval/src/transform/validate.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_infer/messages.ftl | 2 | ||||
| -rw-r--r-- | compiler/rustc_infer/src/errors/mod.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_infer/src/infer/error_reporting/mod.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_lexer/src/cursor.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/coroutine/by_move_body.rs | 9 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/inline.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_next_trait_solver/src/canonicalizer.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_parse/messages.ftl | 3 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/errors.rs | 22 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lexer/mod.rs | 57 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lexer/unescape_error_reporting.rs | 20 |
12 files changed, 130 insertions, 44 deletions
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index c8eeaa4a45e..1664475f52a 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -85,7 +85,7 @@ impl<'tcx> MirPass<'tcx> for Validator { cfg_checker.check_cleanup_control_flow(); // Also run the TypeChecker. - for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body) { + for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body, body) { cfg_checker.fail(location, msg); } @@ -541,19 +541,25 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { /// A faster version of the validation pass that only checks those things which may break when /// instantiating any generic parameters. +/// +/// `caller_body` is used to detect cycles in MIR inlining and MIR validation before +/// `optimized_mir` is available. pub fn validate_types<'tcx>( tcx: TyCtxt<'tcx>, mir_phase: MirPhase, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, + caller_body: &Body<'tcx>, ) -> Vec<(Location, String)> { - let mut type_checker = TypeChecker { body, tcx, param_env, mir_phase, failures: Vec::new() }; + let mut type_checker = + TypeChecker { body, caller_body, tcx, param_env, mir_phase, failures: Vec::new() }; type_checker.visit_body(body); type_checker.failures } struct TypeChecker<'a, 'tcx> { body: &'a Body<'tcx>, + caller_body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, mir_phase: MirPhase, @@ -705,8 +711,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } &ty::Coroutine(def_id, args) => { let f_ty = if let Some(var) = parent_ty.variant_index { - let gen_body = if def_id == self.body.source.def_id() { - self.body + // If we're currently validating an inlined copy of this body, + // then it will no longer be parameterized over the original + // args of the coroutine. Otherwise, we prefer to use this body + // since we may be in the process of computing this MIR in the + // first place. + let gen_body = if def_id == self.caller_body.source.def_id() { + self.caller_body } else { self.tcx.optimized_mir(def_id) }; diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index e44a6ae3b3f..521c65c6009 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -169,7 +169,7 @@ infer_lifetime_param_suggestion_elided = each elided lifetime in input position infer_meant_byte_literal = if you meant to write a byte literal, prefix with `b` infer_meant_char_literal = if you meant to write a `char` literal, use single quotes -infer_meant_str_literal = if you meant to write a `str` literal, use double quotes +infer_meant_str_literal = if you meant to write a string literal, use double quotes infer_mismatched_static_lifetime = incompatible lifetime on type infer_more_targeted = {$has_param_name -> [true] `{$param_name}` diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index a3cf0d8e520..d0b1f2848ff 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1339,15 +1339,12 @@ pub enum TypeErrorAdditionalDiags { span: Span, code: String, }, - #[suggestion( - infer_meant_str_literal, - code = "\"{code}\"", - applicability = "machine-applicable" - )] + #[multipart_suggestion(infer_meant_str_literal, applicability = "machine-applicable")] MeantStrLiteral { - #[primary_span] - span: Span, - code: String, + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, }, #[suggestion( infer_consider_specifying_length, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 4364249100d..82634f7308d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2079,16 +2079,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // If a string was expected and the found expression is a character literal, // perhaps the user meant to write `"s"` to specify a string literal. (ty::Ref(_, r, _), ty::Char) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { - if let Some(code) = - code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) - { - suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { - span, - code: escape_literal(code), - }) - } - } + suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { + start: span.with_hi(span.lo() + BytePos(1)), + end: span.with_lo(span.hi() - BytePos(1)), + }) } // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`, // we try to suggest to add the missing `let` for `if let Some(..) = expr` diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index aba7f95487e..d173c3ac032 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -46,7 +46,7 @@ impl<'a> Cursor<'a> { /// If requested position doesn't exist, `EOF_CHAR` is returned. /// However, getting `EOF_CHAR` doesn't always mean actual end of file, /// it should be checked with `is_eof` method. - pub(crate) fn first(&self) -> char { + pub fn first(&self) -> char { // `.next()` optimizes better than `.nth(0)` self.chars.clone().next().unwrap_or(EOF_CHAR) } @@ -59,6 +59,15 @@ impl<'a> Cursor<'a> { iter.next().unwrap_or(EOF_CHAR) } + /// Peeks the third symbol from the input stream without consuming it. + pub fn third(&self) -> char { + // `.next()` optimizes better than `.nth(1)` + let mut iter = self.chars.clone(); + iter.next(); + iter.next(); + iter.next().unwrap_or(EOF_CHAR) + } + /// Checks if there is nothing more to consume. pub(crate) fn is_eof(&self) -> bool { self.chars.as_str().is_empty() diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 000b96ee801..e0bbd582d88 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -64,12 +64,9 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { let mut by_move_body = body.clone(); MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body); dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); - by_move_body.source = mir::MirSource { - instance: InstanceDef::CoroutineKindShim { - coroutine_def_id: coroutine_def_id.to_def_id(), - }, - promoted: None, - }; + by_move_body.source = mir::MirSource::from_instance(InstanceDef::CoroutineKindShim { + coroutine_def_id: coroutine_def_id.to_def_id(), + }); body.coroutine.as_mut().unwrap().by_move_body = Some(by_move_body); } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 4ec76eec3a9..78c0615b165 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -213,6 +213,7 @@ impl<'tcx> Inliner<'tcx> { MirPhase::Runtime(RuntimePhase::Optimized), self.param_env, &callee_body, + &caller_body, ) .is_empty() { diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index bd23b90fc31..16d8453ea24 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -239,7 +239,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I> // FIXME: We should investigate the perf implications of not uniquifying // `ReErased`. We may be able to short-circuit registering region // obligations if we encounter a `ReErased` on one side, for example. - ty::ReStatic | ty::ReErased => match self.canonicalize_mode { + ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => return r, }, @@ -277,7 +277,6 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I> } } } - ty::ReError(_) => return r, }; let existing_bound_var = match self.canonicalize_mode { diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index e1d26f090e0..aa735f3de1f 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -568,7 +568,7 @@ parse_more_than_one_char = character literal may only contain one codepoint .remove_non = consider removing the non-printing characters .use_double_quotes = if you meant to write a {$is_byte -> [true] byte string - *[false] `str` + *[false] string } literal, use double quotes parse_multiple_skipped_lines = multiple lines skipped by escaped newline @@ -833,6 +833,7 @@ parse_unknown_prefix = prefix `{$prefix}` is unknown .label = unknown prefix .note = prefixed identifiers and literals are reserved since Rust 2021 .suggestion_br = use `br` for a raw byte string + .suggestion_str = if you meant to write a string literal, use double quotes .suggestion_whitespace = consider inserting whitespace here parse_unknown_start_of_token = unknown start of token: {$escaped} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 5cad3a568ee..20ebfc6691b 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1987,6 +1987,17 @@ pub enum UnknownPrefixSugg { style = "verbose" )] Whitespace(#[primary_span] Span), + #[multipart_suggestion( + parse_suggestion_str, + applicability = "maybe-incorrect", + style = "verbose" + )] + MeantStr { + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + }, } #[derive(Diagnostic)] @@ -2198,12 +2209,21 @@ pub enum MoreThanOneCharSugg { ch: String, }, #[suggestion(parse_use_double_quotes, code = "{sugg}", applicability = "machine-applicable")] - Quotes { + QuotesFull { #[primary_span] span: Span, is_byte: bool, sugg: String, }, + #[multipart_suggestion(parse_use_double_quotes, applicability = "machine-applicable")] + Quotes { + #[suggestion_part(code = "{prefix}\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + is_byte: bool, + prefix: &'static str, + }, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f57945a52df..63b2b47630b 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -63,6 +63,7 @@ pub(crate) fn parse_token_trees<'psess, 'src>( cursor, override_span, nbsp_is_whitespace: false, + last_lifetime: None, }; let (stream, res, unmatched_delims) = tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); @@ -105,6 +106,10 @@ struct StringReader<'psess, 'src> { /// in this file, it's safe to treat further occurrences of the non-breaking /// space character as whitespace. nbsp_is_whitespace: bool, + + /// Track the `Span` for the leading `'` of the last lifetime. Used for + /// diagnostics to detect possible typo where `"` was meant. + last_lifetime: Option<Span>, } impl<'psess, 'src> StringReader<'psess, 'src> { @@ -130,6 +135,18 @@ impl<'psess, 'src> StringReader<'psess, 'src> { debug!("next_token: {:?}({:?})", token.kind, self.str_from(start)); + if let rustc_lexer::TokenKind::Semi + | rustc_lexer::TokenKind::LineComment { .. } + | rustc_lexer::TokenKind::BlockComment { .. } + | rustc_lexer::TokenKind::CloseParen + | rustc_lexer::TokenKind::CloseBrace + | rustc_lexer::TokenKind::CloseBracket = token.kind + { + // Heuristic: we assume that it is unlikely we're dealing with an unterminated + // string surrounded by single quotes. + self.last_lifetime = None; + } + // Now "cook" the token, converting the simple `rustc_lexer::TokenKind` enum into a // rich `rustc_ast::TokenKind`. This turns strings into interned symbols and runs // additional validation. @@ -247,6 +264,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { // expansion purposes. See #12512 for the gory details of why // this is necessary. let lifetime_name = self.str_from(start); + self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); if starts_with_number { let span = self.mk_sp(start, self.pos); self.dcx().struct_err("lifetimes cannot start with a number") @@ -395,10 +413,21 @@ impl<'psess, 'src> StringReader<'psess, 'src> { match kind { rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { - self.dcx() + let mut err = self + .dcx() .struct_span_fatal(self.mk_sp(start, end), "unterminated character literal") - .with_code(E0762) - .emit() + .with_code(E0762); + if let Some(lt_sp) = self.last_lifetime { + err.multipart_suggestion( + "if you meant to write a string literal, use double quotes", + vec![ + (lt_sp, "\"".to_string()), + (self.mk_sp(start, start + BytePos(1)), "\"".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + err.emit() } self.cook_unicode(token::Char, Mode::Char, start, end, 1, 1) // ' ' } @@ -669,15 +698,33 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let expn_data = prefix_span.ctxt().outer_expn_data(); if expn_data.edition >= Edition::Edition2021 { + let mut silence = false; // In Rust 2021, this is a hard error. let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) } else if expn_data.is_root() { - Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + if self.cursor.first() == '\'' + && let Some(start) = self.last_lifetime + && self.cursor.third() != '\'' + { + // An "unclosed `char`" error will be emitted already, silence redundant error. + silence = true; + Some(errors::UnknownPrefixSugg::MeantStr { + start, + end: self.mk_sp(self.pos, self.pos + BytePos(1)), + }) + } else { + Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + } } else { None }; - self.dcx().emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg }); + let err = errors::UnknownPrefix { span: prefix_span, prefix, sugg }; + if silence { + self.dcx().create_err(err).delay_as_bug(); + } else { + self.dcx().emit_err(err); + } } else { // Before Rust 2021, only emit a lint for migration. self.psess.buffer_lint_with_diagnostic( diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 3ebad6a9fd7..fa242a32a18 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -95,11 +95,21 @@ pub(crate) fn emit_unescape_error( } escaped.push(c); } - let sugg = format!("{prefix}\"{escaped}\""); - MoreThanOneCharSugg::Quotes { - span: full_lit_span, - is_byte: mode == Mode::Byte, - sugg, + if escaped.len() != lit.len() || full_lit_span.is_empty() { + let sugg = format!("{prefix}\"{escaped}\""); + MoreThanOneCharSugg::QuotesFull { + span: full_lit_span, + is_byte: mode == Mode::Byte, + sugg, + } + } else { + MoreThanOneCharSugg::Quotes { + start: full_lit_span + .with_hi(full_lit_span.lo() + BytePos((prefix.len() + 1) as u32)), + end: full_lit_span.with_lo(full_lit_span.hi() - BytePos(1)), + is_byte: mode == Mode::Byte, + prefix, + } } }); dcx.emit_err(UnescapeError::MoreThanOneChar { |
