Skip to content

Rollup of 12 pull requests #89158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Sep 22, 2021
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0d8245b
Improve diagnostics if a character literal contains combining marks
FabianWolff Sep 9, 2021
71e2eac
Stabilize `Iterator::map_while`
WaffleLapkin Sep 17, 2021
cccd6e0
EscapeDefault: change `range` field to `Range<u8>`, reducing struct s…
klensy Sep 16, 2021
7c8f4f7
Remove needless hir Map ref
camsteffen Sep 18, 2021
a627b4f
[bootstrap] Improve the error message when `ninja` is not found to li…
daira Sep 19, 2021
23d6437
Restore the suggestion to set `ninja = false`.
daira Sep 19, 2021
23c608f
Fix a technicality regarding the size of C's `char` type
dequbed Sep 20, 2021
5fdb9e4
no ensure
BoxyUwU Sep 20, 2021
514b8ba
:arrow_up: rust-analyzer
lnicola Sep 20, 2021
402ebc7
Fix ICE when `indirect_structural_match` is allowed
FabianWolff Sep 20, 2021
33766ae
Impl `Error` for `FromSecsError` without foreign type
mbartlett21 Sep 21, 2021
d4ee698
Fix match for placeholder region
Sep 21, 2021
e4faf17
Re-export FromSecsError from `std`
mbartlett21 Sep 21, 2021
999888c
add case for checking const refs in check_const_value_eq
b-naber Sep 21, 2021
c2cdba4
Rollup merge of #88795 - FabianWolff:issue-88684, r=wesleywiser
the8472 Sep 21, 2021
051168b
Rollup merge of #89015 - klensy:escape-def, r=Mark-Simulacrum
the8472 Sep 21, 2021
9f50c87
Rollup merge of #89078 - camsteffen:map-ref, r=cjgillot
the8472 Sep 21, 2021
d7de8d2
Rollup merge of #89086 - WaffleLapkin:stabilize_iter_map_while, r=ken…
the8472 Sep 21, 2021
a8633eb
Rollup merge of #89096 - daira:improve-ninja-error-message, r=jyn514
the8472 Sep 21, 2021
ecfdadc
Rollup merge of #89113 - BoxyUwU:incr-comp-thir-act, r=lcnr
the8472 Sep 21, 2021
8a6e9cf
Rollup merge of #89114 - dequbed:c-char, r=yaahc
the8472 Sep 21, 2021
74cdd64
Rollup merge of #89115 - lnicola:rust-analyzer-2021-09-20, r=Mark-Sim…
the8472 Sep 21, 2021
8d95bb2
Rollup merge of #89126 - FabianWolff:issue-89088, r=petrochenkov
the8472 Sep 21, 2021
17c9a22
Rollup merge of #89141 - mbartlett21:patch-2, r=kennytm
the8472 Sep 21, 2021
aca790b
Rollup merge of #89142 - nerandell:master, r=jackh726
the8472 Sep 21, 2021
a3e6c19
Rollup merge of #89147 - b-naber:refs_in_check_const_value_eq, r=oli-obk
the8472 Sep 21, 2021
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
1 change: 0 additions & 1 deletion compiler/rustc_data_structures/src/lib.rs
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@
#![feature(extend_one)]
#![feature(hash_raw_entry)]
#![feature(in_band_lifetimes)]
#![feature(iter_map_while)]
#![feature(maybe_uninit_uninit_array)]
#![feature(min_specialization)]
#![feature(never_type)]
16 changes: 8 additions & 8 deletions compiler/rustc_middle/src/hir/map/mod.rs
Original file line number Diff line number Diff line change
@@ -83,12 +83,12 @@ pub struct Map<'hir> {

/// An iterator that walks up the ancestor tree of a given `HirId`.
/// Constructed using `tcx.hir().parent_iter(hir_id)`.
pub struct ParentHirIterator<'map, 'hir> {
pub struct ParentHirIterator<'hir> {
current_id: HirId,
map: &'map Map<'hir>,
map: Map<'hir>,
}

impl<'hir> Iterator for ParentHirIterator<'_, 'hir> {
impl<'hir> Iterator for ParentHirIterator<'hir> {
type Item = (HirId, Node<'hir>);

fn next(&mut self) -> Option<Self::Item> {
@@ -115,12 +115,12 @@ impl<'hir> Iterator for ParentHirIterator<'_, 'hir> {

/// An iterator that walks up the ancestor tree of a given `HirId`.
/// Constructed using `tcx.hir().parent_owner_iter(hir_id)`.
pub struct ParentOwnerIterator<'map, 'hir> {
pub struct ParentOwnerIterator<'hir> {
current_id: HirId,
map: &'map Map<'hir>,
map: Map<'hir>,
}

impl<'hir> Iterator for ParentOwnerIterator<'_, 'hir> {
impl<'hir> Iterator for ParentOwnerIterator<'hir> {
type Item = (HirId, OwnerNode<'hir>);

fn next(&mut self) -> Option<Self::Item> {
@@ -588,13 +588,13 @@ impl<'hir> Map<'hir> {

/// Returns an iterator for the nodes in the ancestor tree of the `current_id`
/// until the crate root is reached. Prefer this over your own loop using `get_parent_node`.
pub fn parent_iter(&self, current_id: HirId) -> ParentHirIterator<'_, 'hir> {
pub fn parent_iter(self, current_id: HirId) -> ParentHirIterator<'hir> {
ParentHirIterator { current_id, map: self }
}

/// Returns an iterator for the nodes in the ancestor tree of the `current_id`
/// until the crate root is reached. Prefer this over your own loop using `get_parent_node`.
pub fn parent_owner_iter(&self, current_id: HirId) -> ParentOwnerIterator<'_, 'hir> {
pub fn parent_owner_iter(self, current_id: HirId) -> ParentOwnerIterator<'hir> {
ParentOwnerIterator { current_id, map: self }
}

5 changes: 4 additions & 1 deletion compiler/rustc_middle/src/ich/impls_ty.rs
Original file line number Diff line number Diff line change
@@ -90,7 +90,10 @@ impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
ty::ReFree(ref free_region) => {
free_region.hash_stable(hcx, hasher);
}
ty::ReVar(..) | ty::RePlaceholder(..) => {
ty::RePlaceholder(p) => {
p.hash_stable(hcx, hasher);
}
ty::ReVar(..) => {
bug!("StableHasher: unexpected region {:?}", *self)
}
}
9 changes: 9 additions & 0 deletions compiler/rustc_middle/src/ty/relate.rs
Original file line number Diff line number Diff line change
@@ -639,6 +639,15 @@ fn check_const_value_eq<R: TypeRelation<'tcx>>(
get_slice_bytes(&tcx, a_val) == get_slice_bytes(&tcx, b_val)
}

(ConstValue::ByRef { alloc: alloc_a, .. }, ConstValue::ByRef { alloc: alloc_b, .. })
if a.ty.is_ref() || b.ty.is_ref() =>
{
if a.ty.is_ref() && b.ty.is_ref() {
alloc_a == alloc_b
} else {
false
}
}
(ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => {
let a_destructured = tcx.destructure_const(relation.param_env().and(a));
let b_destructured = tcx.destructure_const(relation.param_env().and(b));
9 changes: 6 additions & 3 deletions compiler/rustc_mir_build/src/build/mod.rs
Original file line number Diff line number Diff line change
@@ -44,15 +44,18 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_
let body_owner_kind = tcx.hir().body_owner_kind(id);
let typeck_results = tcx.typeck_opt_const_arg(def);

// Ensure unsafeck is ran before we steal the THIR.
// Ensure unsafeck and abstract const building is ran before we steal the THIR.
// We can't use `ensure()` for `thir_abstract_const` as it doesn't compute the query
// if inputs are green. This can cause ICEs when calling `thir_abstract_const` after
// THIR has been stolen if we haven't computed this query yet.
match def {
ty::WithOptConstParam { did, const_param_did: Some(const_param_did) } => {
tcx.ensure().thir_check_unsafety_for_const_arg((did, const_param_did));
tcx.ensure().thir_abstract_const_of_const_arg((did, const_param_did));
drop(tcx.thir_abstract_const_of_const_arg((did, const_param_did)));
}
ty::WithOptConstParam { did, const_param_did: None } => {
tcx.ensure().thir_check_unsafety(did);
tcx.ensure().thir_abstract_const(did);
drop(tcx.thir_abstract_const(did));
}
}

14 changes: 8 additions & 6 deletions compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
@@ -322,16 +322,18 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
&& !self.saw_const_match_lint.get()
{
self.saw_const_match_lint.set(true);
let msg = format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
cv.ty, cv.ty,
);
tcx.struct_span_lint_hir(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
id,
span,
|lint| lint.build(&msg).emit(),
|lint| {
let msg = format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
cv.ty, cv.ty,
);
lint.build(&msg).emit()
},
);
}
// Since we are behind a reference, we can just bubble the error up so we get a
61 changes: 47 additions & 14 deletions compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
use std::iter::once;
use std::ops::Range;

use rustc_errors::{Applicability, Handler};
use rustc_errors::{pluralize, Applicability, Handler};
use rustc_lexer::unescape::{EscapeError, Mode};
use rustc_span::{BytePos, Span};

@@ -49,24 +49,57 @@ pub(crate) fn emit_unescape_error(
.emit();
}
EscapeError::MoreThanOneChar => {
let (prefix, msg) = if mode.is_bytes() {
("b", "if you meant to write a byte string literal, use double quotes")
} else {
("", "if you meant to write a `str` literal, use double quotes")
};
use unicode_normalization::{char::is_combining_mark, UnicodeNormalization};

handler
.struct_span_err(
span_with_quotes,
"character literal may only contain one codepoint",
)
.span_suggestion(
let mut has_help = false;
let mut handler = handler.struct_span_err(
span_with_quotes,
"character literal may only contain one codepoint",
);

if lit.chars().skip(1).all(|c| is_combining_mark(c)) {
let escaped_marks =
lit.chars().skip(1).map(|c| c.escape_default().to_string()).collect::<Vec<_>>();
handler.span_note(
span,
&format!(
"this `{}` is followed by the combining mark{} `{}`",
lit.chars().next().unwrap(),
pluralize!(escaped_marks.len()),
escaped_marks.join(""),
),
);
let normalized = lit.nfc().to_string();
if normalized.chars().count() == 1 {
has_help = true;
handler.span_suggestion(
span,
&format!(
"consider using the normalized form `{}` of this character",
normalized.chars().next().unwrap().escape_default()
),
normalized,
Applicability::MachineApplicable,
);
}
}

if !has_help {
let (prefix, msg) = if mode.is_bytes() {
("b", "if you meant to write a byte string literal, use double quotes")
} else {
("", "if you meant to write a `str` literal, use double quotes")
};

handler.span_suggestion(
span_with_quotes,
msg,
format!("{}\"{}\"", prefix, lit),
Applicability::MachineApplicable,
)
.emit();
);
}

handler.emit();
}
EscapeError::EscapeOnlyChar => {
let (c, char_span) = last_char();
3 changes: 1 addition & 2 deletions compiler/rustc_resolve/src/late/lifetimes.rs
Original file line number Diff line number Diff line change
@@ -522,8 +522,7 @@ fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> LocalDefId {
_ => {}
}
let item = {
let hir = tcx.hir();
let mut parent_iter = hir.parent_iter(hir_id);
let mut parent_iter = tcx.hir().parent_iter(hir_id);
loop {
let node = parent_iter.next().map(|n| n.1);
match node {
1 change: 0 additions & 1 deletion library/alloc/tests/lib.rs
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@
#![feature(binary_heap_retain)]
#![feature(binary_heap_as_slice)]
#![feature(inplace_iteration)]
#![feature(iter_map_while)]
#![feature(slice_group_by)]
#![feature(slice_partition_dedup)]
#![feature(vec_spare_capacity)]
10 changes: 6 additions & 4 deletions library/core/src/ascii.rs
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ use crate::str::from_utf8_unchecked;
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Clone)]
pub struct EscapeDefault {
range: Range<usize>,
range: Range<u8>,
data: [u8; 4],
}

@@ -114,7 +114,7 @@ pub fn escape_default(c: u8) -> EscapeDefault {
impl Iterator for EscapeDefault {
type Item = u8;
fn next(&mut self) -> Option<u8> {
self.range.next().map(|i| self.data[i])
self.range.next().map(|i| self.data[i as usize])
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
@@ -126,7 +126,7 @@ impl Iterator for EscapeDefault {
#[stable(feature = "rust1", since = "1.0.0")]
impl DoubleEndedIterator for EscapeDefault {
fn next_back(&mut self) -> Option<u8> {
self.range.next_back().map(|i| self.data[i])
self.range.next_back().map(|i| self.data[i as usize])
}
}
#[stable(feature = "rust1", since = "1.0.0")]
@@ -138,7 +138,9 @@ impl FusedIterator for EscapeDefault {}
impl fmt::Display for EscapeDefault {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// SAFETY: ok because `escape_default` created only valid utf-8 data
f.write_str(unsafe { from_utf8_unchecked(&self.data[self.range.clone()]) })
f.write_str(unsafe {
from_utf8_unchecked(&self.data[(self.range.start as usize)..(self.range.end as usize)])
})
}
}

6 changes: 3 additions & 3 deletions library/core/src/iter/adapters/map_while.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ use crate::ops::{ControlFlow, Try};
/// [`map_while`]: Iterator::map_while
/// [`Iterator`]: trait.Iterator.html
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
#[stable(feature = "iter_map_while", since = "1.57.0")]
#[derive(Clone)]
pub struct MapWhile<I, P> {
iter: I,
@@ -23,14 +23,14 @@ impl<I, P> MapWhile<I, P> {
}
}

#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
#[stable(feature = "iter_map_while", since = "1.57.0")]
impl<I: fmt::Debug, P> fmt::Debug for MapWhile<I, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MapWhile").field("iter", &self.iter).finish()
}
}

#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
#[stable(feature = "iter_map_while", since = "1.57.0")]
impl<B, I: Iterator, P> Iterator for MapWhile<I, P>
where
P: FnMut(I::Item) -> Option<B>,
2 changes: 1 addition & 1 deletion library/core/src/iter/adapters/mod.rs
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ pub use self::copied::Copied;
#[stable(feature = "iter_intersperse", since = "1.56.0")]
pub use self::intersperse::{Intersperse, IntersperseWith};

#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
#[stable(feature = "iter_map_while", since = "1.57.0")]
pub use self::map_while::MapWhile;

#[unstable(feature = "trusted_random_access", issue = "none")]
2 changes: 1 addition & 1 deletion library/core/src/iter/mod.rs
Original file line number Diff line number Diff line change
@@ -399,7 +399,7 @@ pub use self::adapters::Cloned;
pub use self::adapters::Copied;
#[stable(feature = "iterator_flatten", since = "1.29.0")]
pub use self::adapters::Flatten;
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
#[stable(feature = "iter_map_while", since = "1.57.0")]
pub use self::adapters::MapWhile;
#[unstable(feature = "inplace_iteration", issue = "none")]
pub use self::adapters::SourceIter;
5 changes: 1 addition & 4 deletions library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
@@ -1116,7 +1116,6 @@ pub trait Iterator {
/// Basic usage:
///
/// ```
/// #![feature(iter_map_while)]
/// let a = [-1i32, 4, 0, 1];
///
/// let mut iter = a.iter().map_while(|x| 16i32.checked_div(*x));
@@ -1147,7 +1146,6 @@ pub trait Iterator {
/// Stopping after an initial [`None`]:
///
/// ```
/// #![feature(iter_map_while)]
/// use std::convert::TryFrom;
///
/// let a = [0, 1, 2, -3, 4, 5, -6];
@@ -1165,7 +1163,6 @@ pub trait Iterator {
/// removed:
///
/// ```
/// #![feature(iter_map_while)]
/// use std::convert::TryFrom;
///
/// let a = [1, 2, -3, 4];
@@ -1191,7 +1188,7 @@ pub trait Iterator {
///
/// [`fuse`]: Iterator::fuse
#[inline]
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
#[stable(feature = "iter_map_while", since = "1.57.0")]
fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
where
Self: Sized,
1 change: 0 additions & 1 deletion library/core/tests/lib.rs
Original file line number Diff line number Diff line change
@@ -50,7 +50,6 @@
#![feature(iter_partition_in_place)]
#![feature(iter_is_partitioned)]
#![feature(iter_order_by)]
#![feature(iter_map_while)]
#![feature(const_mut_refs)]
#![feature(const_pin)]
#![feature(const_slice_from_raw_parts)]
3 changes: 2 additions & 1 deletion library/std/src/error.rs
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ use crate::num;
use crate::str;
use crate::string;
use crate::sync::Arc;
use crate::time;

/// `Error` is a trait representing the basic expectations for error values,
/// i.e., values of type `E` in [`Result<T, E>`].
@@ -598,7 +599,7 @@ impl Error for char::ParseCharError {
impl Error for alloc::collections::TryReserveError {}

#[unstable(feature = "duration_checked_float", issue = "83400")]
impl Error for core::time::FromSecsError {}
impl Error for time::FromSecsError {}

// Copied from `any.rs`.
impl dyn Error + 'static {
2 changes: 1 addition & 1 deletion library/std/src/os/raw/char.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Equivalent to C's `char` type.

[C's `char` type] is completely unlike [Rust's `char` type]; while Rust's type represents a unicode scalar value, C's `char` type is just an ordinary integer. This type will always be either [`i8`] or [`u8`], as the type is defined as being one byte long.
[C's `char` type] is completely unlike [Rust's `char` type]; while Rust's type represents a unicode scalar value, C's `char` type is just an ordinary integer. On modern architectures this type will always be either [`i8`] or [`u8`], as they use byte-addresses memory with 8-bit bytes.

C chars are most commonly used to make C strings. Unlike Rust, where the length of a string is included alongside the string, C strings mark the end of a string with the character `'\0'`. See [`CStr`] for more information.

3 changes: 3 additions & 0 deletions library/std/src/time.rs
Original file line number Diff line number Diff line change
@@ -44,6 +44,9 @@ use crate::sys_common::FromInner;
#[stable(feature = "time", since = "1.3.0")]
pub use core::time::Duration;

#[unstable(feature = "duration_checked_float", issue = "83400")]
pub use core::time::FromSecsError;

/// A measurement of a monotonically nondecreasing clock.
/// Opaque and useful only with [`Duration`].
///
9 changes: 7 additions & 2 deletions src/bootstrap/lib.rs
Original file line number Diff line number Diff line change
@@ -1494,8 +1494,13 @@ impl Build {
{
eprintln!(
"
Couldn't find required command: ninja
You should install ninja, or set `ninja=false` in config.toml in the `[llvm]` section.
Couldn't find required command: ninja (or ninja-build)
You should install ninja as described at
<https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages>,
or set `ninja = false` in the `[llvm]` section of `config.toml`.
Alternatively, set `download-ci-llvm = true` in that `[llvm]` section
to download LLVM rather than building it.
"
);
std::process::exit(1);
22 changes: 22 additions & 0 deletions src/test/ui/consts/issue-89088.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Regression test for the ICE described in #89088.

// check-pass

#![allow(indirect_structural_match)]
use std::borrow::Cow;

const FOO: &A = &A::Field(Cow::Borrowed("foo"));

#[derive(PartialEq, Eq)]
enum A {
Field(Cow<'static, str>)
}

fn main() {
let var = A::Field(Cow::Borrowed("bar"));

match &var {
FOO => todo!(),
_ => todo!()
}
}
25 changes: 25 additions & 0 deletions src/test/ui/consts/refs_check_const_eq-issue-88384.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// check-pass

#![feature(fn_traits)]
#![feature(adt_const_params)]
//~^ WARNING the feature `adt_const_params` is incomplete

#[derive(PartialEq, Eq)]
struct CompileTimeSettings{
hooks: &'static[fn()],
}

struct Foo<const T: CompileTimeSettings>;

impl<const T: CompileTimeSettings> Foo<T> {
fn call_hooks(){
}
}

fn main(){
const SETTINGS: CompileTimeSettings = CompileTimeSettings{
hooks: &[],
};

Foo::<SETTINGS>::call_hooks();
}
11 changes: 11 additions & 0 deletions src/test/ui/consts/refs_check_const_eq-issue-88384.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/refs_check_const_eq-issue-88384.rs:4:12
|
LL | #![feature(adt_const_params)]
| ^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information

warning: 1 warning emitted

12 changes: 12 additions & 0 deletions src/test/ui/consts/refs_check_const_value_eq-issue-88876.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// check-pass

#![allow(incomplete_features)]
#![feature(adt_const_params)]

struct FooConst<const ARRAY: &'static [&'static str]> {}

const FOO_ARR: &[&'static str; 2] = &["Hello", "Friend"];

fn main() {
let _ = FooConst::<FOO_ARR> {};
}
21 changes: 21 additions & 0 deletions src/test/ui/parser/unicode-character-literal.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Regression test for #88684: Improve diagnostics for combining marks
// in character literals.

// run-rustfix

fn main() {
let _spade = "♠️";
//~^ ERROR: character literal may only contain one codepoint
//~| NOTE: this `♠` is followed by the combining mark `\u{fe0f}`
//~| HELP: if you meant to write a `str` literal, use double quotes

let _s = "ṩ̂̊";
//~^ ERROR: character literal may only contain one codepoint
//~| NOTE: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}`
//~| HELP: if you meant to write a `str` literal, use double quotes

let _a = 'Å';
//~^ ERROR: character literal may only contain one codepoint
//~| NOTE: this `A` is followed by the combining mark `\u{30a}`
//~| HELP: consider using the normalized form `\u{c5}` of this character
}
21 changes: 21 additions & 0 deletions src/test/ui/parser/unicode-character-literal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Regression test for #88684: Improve diagnostics for combining marks
// in character literals.

// run-rustfix

fn main() {
let _spade = '♠️';
//~^ ERROR: character literal may only contain one codepoint
//~| NOTE: this `♠` is followed by the combining mark `\u{fe0f}`
//~| HELP: if you meant to write a `str` literal, use double quotes

let _s = 'ṩ̂̊';
//~^ ERROR: character literal may only contain one codepoint
//~| NOTE: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}`
//~| HELP: if you meant to write a `str` literal, use double quotes

let _a = '';
//~^ ERROR: character literal may only contain one codepoint
//~| NOTE: this `A` is followed by the combining mark `\u{30a}`
//~| HELP: consider using the normalized form `\u{c5}` of this character
}
48 changes: 48 additions & 0 deletions src/test/ui/parser/unicode-character-literal.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
error: character literal may only contain one codepoint
--> $DIR/unicode-character-literal.rs:7:18
|
LL | let _spade = '♠️';
| ^^^
|
note: this `♠` is followed by the combining mark `\u{fe0f}`
--> $DIR/unicode-character-literal.rs:7:19
|
LL | let _spade = '♠️';
| ^
help: if you meant to write a `str` literal, use double quotes
|
LL | let _spade = "♠️";
| ~~~

error: character literal may only contain one codepoint
--> $DIR/unicode-character-literal.rs:12:14
|
LL | let _s = 'ṩ̂̊';
| ^^^
|
note: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}`
--> $DIR/unicode-character-literal.rs:12:15
|
LL | let _s = 'ṩ̂̊';
| ^
help: if you meant to write a `str` literal, use double quotes
|
LL | let _s = "ṩ̂̊";
| ~~~

error: character literal may only contain one codepoint
--> $DIR/unicode-character-literal.rs:17:14
|
LL | let _a = 'Å';
| ^-^
| |
| help: consider using the normalized form `\u{c5}` of this character: `Å`
|
note: this `A` is followed by the combining mark `\u{30a}`
--> $DIR/unicode-character-literal.rs:17:15
|
LL | let _a = 'Å';
| ^

error: aborting due to 3 previous errors

3 changes: 1 addition & 2 deletions src/tools/clippy/clippy_utils/src/higher.rs
Original file line number Diff line number Diff line change
@@ -105,8 +105,7 @@ impl<'hir> IfLet<'hir> {
if_else,
) = expr.kind
{
let hir = cx.tcx.hir();
let mut iter = hir.parent_iter(expr.hir_id);
let mut iter = cx.tcx.hir().parent_iter(expr.hir_id);
if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() {
if let Some((
_,
15 changes: 5 additions & 10 deletions src/tools/clippy/clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -833,12 +833,11 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind
ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
));

let map = cx.tcx.hir();
let mut child_id = e.hir_id;
let mut capture = CaptureKind::Value;
let mut capture_expr_ty = e;

for (parent_id, parent) in map.parent_iter(e.hir_id) {
for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
if let [Adjustment {
kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
target,
@@ -1224,8 +1223,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio

/// Gets the loop or closure enclosing the given expression, if any.
pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
let map = tcx.hir();
for (_, node) in map.parent_iter(expr.hir_id) {
for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
match node {
Node::Expr(
e
@@ -1244,8 +1242,7 @@ pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Opti

/// Gets the parent node if it's an impl block.
pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
let map = tcx.hir();
match map.parent_iter(id).next() {
match tcx.hir().parent_iter(id).next() {
Some((
_,
Node::Item(Item {
@@ -1259,8 +1256,7 @@ pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {

/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
let map = tcx.hir();
let mut iter = map.parent_iter(expr.hir_id);
let mut iter = tcx.hir().parent_iter(expr.hir_id);
match iter.next() {
Some((
_,
@@ -1794,9 +1790,8 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool

/// Gets the node where an expression is either used, or it's type is unified with another branch.
pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
let map = tcx.hir();
let mut child_id = expr.hir_id;
let mut iter = map.parent_iter(child_id);
let mut iter = tcx.hir().parent_iter(child_id);
loop {
match iter.next() {
None => break None,
2 changes: 1 addition & 1 deletion src/tools/rust-analyzer