Auto merge of #93390 - matthiaskrgr:rollup-4xeki5w, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #91641 (Define c_char using cfg_if rather than repeating 40-line cfg)
 - #92899 (Mention std::iter::zip in Iterator::zip docs)
 - #93193 (Add test for stable hash uniqueness of adjacent field values)
 - #93325 (Introduce a limit to Levenshtein distance computation)
 - #93339 (rustdoc: add test case for multiple traits and erased names)
 - #93357 (Clarify the `usage-of-qualified-ty` error message.)
 - #93363 (`#[rustc_pass_by_value]` cleanup)
 - #93365 (More arena cleanups)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2022-01-27 22:34:34 +00:00
commit 1b4109306c
22 changed files with 322 additions and 180 deletions

View File

@ -45,24 +45,24 @@ pub struct TypedArena<T> {
end: Cell<*mut T>,
/// A vector of arena chunks.
chunks: RefCell<Vec<TypedArenaChunk<T>>>,
chunks: RefCell<Vec<ArenaChunk<T>>>,
/// Marker indicating that dropping the arena causes its owned
/// instances of `T` to be dropped.
_own: PhantomData<T>,
}
struct TypedArenaChunk<T> {
struct ArenaChunk<T = u8> {
/// The raw storage for the arena chunk.
storage: Box<[MaybeUninit<T>]>,
/// The number of valid entries in the chunk.
entries: usize,
}
impl<T> TypedArenaChunk<T> {
impl<T> ArenaChunk<T> {
#[inline]
unsafe fn new(capacity: usize) -> TypedArenaChunk<T> {
TypedArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 }
unsafe fn new(capacity: usize) -> ArenaChunk<T> {
ArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 }
}
/// Destroys this arena chunk.
@ -125,6 +125,11 @@ impl<I, T> IterExt<T> for I
where
I: IntoIterator<Item = T>,
{
// This default collects into a `SmallVec` and then allocates by copying
// from it. The specializations below for types like `Vec` are more
// efficient, copying directly without the intermediate collecting step.
// This default could be made more efficient, like
// `DroplessArena::alloc_from_iter`, but it's not hot enough to bother.
#[inline]
default fn alloc_from_iter(self, arena: &TypedArena<T>) -> &mut [T] {
let vec: SmallVec<[_; 8]> = self.into_iter().collect();
@ -139,7 +144,7 @@ impl<T, const N: usize> IterExt<T> for std::array::IntoIter<T, N> {
if len == 0 {
return &mut [];
}
// Move the content to the arena by copying and then forgetting it
// Move the content to the arena by copying and then forgetting it.
unsafe {
let start_ptr = arena.alloc_raw_slice(len);
self.as_slice().as_ptr().copy_to_nonoverlapping(start_ptr, len);
@ -156,7 +161,7 @@ impl<T> IterExt<T> for Vec<T> {
if len == 0 {
return &mut [];
}
// Move the content to the arena by copying and then forgetting it
// Move the content to the arena by copying and then forgetting it.
unsafe {
let start_ptr = arena.alloc_raw_slice(len);
self.as_ptr().copy_to_nonoverlapping(start_ptr, len);
@ -173,7 +178,7 @@ impl<A: smallvec::Array> IterExt<A::Item> for SmallVec<A> {
if len == 0 {
return &mut [];
}
// Move the content to the arena by copying and then forgetting it
// Move the content to the arena by copying and then forgetting it.
unsafe {
let start_ptr = arena.alloc_raw_slice(len);
self.as_ptr().copy_to_nonoverlapping(start_ptr, len);
@ -272,7 +277,7 @@ impl<T> TypedArena<T> {
// Also ensure that this chunk can fit `additional`.
new_cap = cmp::max(additional, new_cap);
let mut chunk = TypedArenaChunk::<T>::new(new_cap);
let mut chunk = ArenaChunk::<T>::new(new_cap);
self.ptr.set(chunk.start());
self.end.set(chunk.end());
chunks.push(chunk);
@ -281,7 +286,7 @@ impl<T> TypedArena<T> {
// Drops the contents of the last chunk. The last chunk is partially empty, unlike all other
// chunks.
fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk<T>) {
fn clear_last_chunk(&self, last_chunk: &mut ArenaChunk<T>) {
// Determine how much was filled.
let start = last_chunk.start() as usize;
// We obtain the value of the pointer to the first uninitialized element.
@ -340,7 +345,7 @@ pub struct DroplessArena {
end: Cell<*mut u8>,
/// A vector of arena chunks.
chunks: RefCell<Vec<TypedArenaChunk<u8>>>,
chunks: RefCell<Vec<ArenaChunk>>,
}
unsafe impl Send for DroplessArena {}
@ -378,7 +383,7 @@ impl DroplessArena {
// Also ensure that this chunk can fit `additional`.
new_cap = cmp::max(additional, new_cap);
let mut chunk = TypedArenaChunk::<u8>::new(new_cap);
let mut chunk = ArenaChunk::new(new_cap);
self.start.set(chunk.start());
self.end.set(chunk.end());
chunks.push(chunk);
@ -520,10 +525,19 @@ impl DroplessArena {
}
}
// Declare an `Arena` containing one dropless arena and many typed arenas (the
// types of the typed arenas are specified by the arguments). The dropless
// arena will be used for any types that impl `Copy`, and also for any of the
// specified types that satisfy `!mem::needs_drop`.
/// Declare an `Arena` containing one dropless arena and many typed arenas (the
/// types of the typed arenas are specified by the arguments).
///
/// There are three cases of interest.
/// - Types that are `Copy`: these need not be specified in the arguments. They
/// will use the `DroplessArena`.
/// - Types that are `!Copy` and `!Drop`: these must be specified in the
/// arguments. An empty `TypedArena` will be created for each one, but the
/// `DroplessArena` will always be used and the `TypedArena` will stay empty.
/// This is odd but harmless, because an empty arena allocates no memory.
/// - Types that are `!Copy` and `Drop`: these must be specified in the
/// arguments. The `TypedArena` will be used for them.
///
#[rustc_macro_transparency = "semitransparent"]
pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
#[derive(Default)]
@ -532,7 +546,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
$($name: $crate::TypedArena<$ty>,)*
}
pub trait ArenaAllocatable<'tcx, T = Self>: Sized {
pub trait ArenaAllocatable<'tcx, C = rustc_arena::IsNotCopy>: Sized {
fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self;
fn allocate_from_iter<'a>(
arena: &'a Arena<'tcx>,
@ -541,7 +555,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
}
// Any type that impls `Copy` can be arena-allocated in the `DroplessArena`.
impl<'tcx, T: Copy> ArenaAllocatable<'tcx, ()> for T {
impl<'tcx, T: Copy> ArenaAllocatable<'tcx, rustc_arena::IsCopy> for T {
#[inline]
fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self {
arena.dropless.alloc(self)
@ -555,7 +569,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
}
}
$(
impl<'tcx> ArenaAllocatable<'tcx, $ty> for $ty {
impl<'tcx> ArenaAllocatable<'tcx, rustc_arena::IsNotCopy> for $ty {
#[inline]
fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self {
if !::std::mem::needs_drop::<Self>() {
@ -581,7 +595,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
impl<'tcx> Arena<'tcx> {
#[inline]
pub fn alloc<T: ArenaAllocatable<'tcx, U>, U>(&self, value: T) -> &mut T {
pub fn alloc<T: ArenaAllocatable<'tcx, C>, C>(&self, value: T) -> &mut T {
value.allocate_on(self)
}
@ -594,7 +608,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
self.dropless.alloc_slice(value)
}
pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx, U>, U>(
pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx, C>, C>(
&'a self,
iter: impl ::std::iter::IntoIterator<Item = T>,
) -> &'a mut [T] {
@ -603,5 +617,10 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
}
}
// Marker types that let us give different behaviour for arenas allocating
// `Copy` types vs `!Copy` types.
pub struct IsCopy;
pub struct IsNotCopy;
#[cfg(test)]
mod tests;

View File

@ -607,7 +607,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
&mut self,
macro_def: &ast::MacroDef,
ident: &Ident,
sp: &Span,
sp: Span,
print_visibility: impl FnOnce(&mut Self),
) {
let (kw, has_bang) = if macro_def.macro_rules {
@ -623,7 +623,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
macro_def.body.delim(),
&macro_def.body.inner_tokens(),
true,
*sp,
sp,
);
if macro_def.body.need_semicolon() {
self.word(";");

View File

@ -347,7 +347,7 @@ impl<'a> State<'a> {
}
}
ast::ItemKind::MacroDef(ref macro_def) => {
self.print_mac_def(macro_def, &item.ident, &item.span, |state| {
self.print_mac_def(macro_def, &item.ident, item.span, |state| {
state.print_visibility(&item.vis)
});
}

View File

@ -98,3 +98,45 @@ fn test_hash_bit_matrix() {
assert_ne!(a, b);
assert_ne!(hash(&a), hash(&b));
}
// Check that exchanging the value of two adjacent fields changes the hash.
#[test]
fn test_attribute_permutation() {
macro_rules! test_type {
($ty: ty) => {{
struct Foo {
a: $ty,
b: $ty,
}
impl<CTX> HashStable<CTX> for Foo {
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
self.a.hash_stable(hcx, hasher);
self.b.hash_stable(hcx, hasher);
}
}
#[allow(overflowing_literals)]
let mut item = Foo { a: 0xFF, b: 0xFF_FF };
let hash_a = hash(&item);
std::mem::swap(&mut item.a, &mut item.b);
let hash_b = hash(&item);
assert_ne!(
hash_a,
hash_b,
"The hash stayed the same after values were swapped for type `{}`!",
stringify!($ty)
);
}};
}
test_type!(u16);
test_type!(u32);
test_type!(u64);
test_type!(u128);
test_type!(i16);
test_type!(i32);
test_type!(i64);
test_type!(i128);
}

View File

@ -625,7 +625,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
),
rustc_attr!(
rustc_pass_by_value, Normal,
template!(Word), WarnFollowing,
template!(Word), ErrorFollowing,
"#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
),
BuiltinAttribute {

View File

@ -571,7 +571,7 @@ impl<'a> State<'a> {
self.ann.nested(self, Nested::Body(body));
}
hir::ItemKind::Macro(ref macro_def) => {
self.print_mac_def(macro_def, &item.ident, &item.span, |state| {
self.print_mac_def(macro_def, &item.ident, item.span, |state| {
state.print_visibility(&item.vis)
});
}

View File

@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
lint.build(&format!("usage of qualified `ty::{}`", t))
.span_suggestion(
path.span,
"try using it unqualified",
"try importing it and using it unqualified",
t,
// The import probably needs to be changed
Applicability::MaybeIncorrect,

View File

@ -336,5 +336,5 @@ fn is_arg_inside_call(arg: Span, call: Span) -> bool {
// panic call in the source file, to avoid invalid suggestions when macros are involved.
// We specifically check for the spans to not be identical, as that happens sometimes when
// proc_macros lie about spans and apply the same span to all the tokens they produce.
call.contains(arg) && !call.source_equal(&arg)
call.contains(arg) && !call.source_equal(arg)
}

View File

@ -76,10 +76,10 @@ fn gen_args(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> String {
.map(|arg| match arg {
GenericArg::Lifetime(lt) => lt.name.ident().to_string(),
GenericArg::Type(ty) => {
cx.tcx.sess.source_map().span_to_snippet(ty.span).unwrap_or_default()
cx.tcx.sess.source_map().span_to_snippet(ty.span).unwrap_or_else(|_| "_".into())
}
GenericArg::Const(c) => {
cx.tcx.sess.source_map().span_to_snippet(c.span).unwrap_or_default()
cx.tcx.sess.source_map().span_to_snippet(c.span).unwrap_or_else(|_| "_".into())
}
GenericArg::Infer(_) => String::from("_"),
})

View File

@ -230,7 +230,7 @@ where
}
/// Format a string showing the start line and column, and end line and column within a file.
pub fn source_range_no_file<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> String {
pub fn source_range_no_file<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String {
let source_map = tcx.sess.source_map();
let start = source_map.lookup_char_pos(span.lo());
let end = source_map.lookup_char_pos(span.hi());
@ -629,7 +629,7 @@ fn tooltip<'tcx>(
let mut text = Vec::new();
text.push(format!("{}: {}:", spanview_id, &source_map.span_to_embeddable_string(span)));
for statement in statements {
let source_range = source_range_no_file(tcx, &statement.source_info.span);
let source_range = source_range_no_file(tcx, statement.source_info.span);
text.push(format!(
"\n{}{}: {}: {:?}",
TOOLTIP_INDENT,
@ -639,7 +639,7 @@ fn tooltip<'tcx>(
));
}
if let Some(term) = terminator {
let source_range = source_range_no_file(tcx, &term.source_info.span);
let source_range = source_range_no_file(tcx, term.source_info.span);
text.push(format!(
"\n{}{}: {}: {:?}",
TOOLTIP_INDENT,

View File

@ -28,7 +28,7 @@ impl CoverageStatement {
let stmt = &mir_body[bb].statements[stmt_index];
format!(
"{}: @{}[{}]: {:?}",
source_range_no_file(tcx, &span),
source_range_no_file(tcx, span),
bb.index(),
stmt_index,
stmt
@ -38,7 +38,7 @@ impl CoverageStatement {
let term = mir_body[bb].terminator();
format!(
"{}: @{}.{}: {:?}",
source_range_no_file(tcx, &span),
source_range_no_file(tcx, span),
bb.index(),
term_type(&term.kind),
term.kind
@ -155,7 +155,7 @@ impl CoverageSpan {
pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
format!(
"{}\n {}",
source_range_no_file(tcx, &self.span),
source_range_no_file(tcx, self.span),
self.format_coverage_statements(tcx, mir_body).replace('\n', "\n "),
)
}

View File

@ -423,7 +423,7 @@ impl<'a> Parser<'a> {
// Maybe the user misspelled `macro_rules` (issue #91227)
if self.token.is_ident()
&& path.segments.len() == 1
&& lev_distance("macro_rules", &path.segments[0].ident.to_string()) <= 3
&& lev_distance("macro_rules", &path.segments[0].ident.to_string(), 3).is_some()
{
err.span_suggestion(
path.span,

View File

@ -11,16 +11,21 @@ use std::cmp;
mod tests;
/// Finds the Levenshtein distance between two strings.
pub fn lev_distance(a: &str, b: &str) -> usize {
// cases which don't require further computation
if a.is_empty() {
return b.chars().count();
} else if b.is_empty() {
return a.chars().count();
///
/// Returns None if the distance exceeds the limit.
pub fn lev_distance(a: &str, b: &str, limit: usize) -> Option<usize> {
let n = a.chars().count();
let m = b.chars().count();
let min_dist = if n < m { m - n } else { n - m };
if min_dist > limit {
return None;
}
if n == 0 || m == 0 {
return (min_dist <= limit).then_some(min_dist);
}
let mut dcol: Vec<_> = (0..=b.len()).collect();
let mut t_last = 0;
let mut dcol: Vec<_> = (0..=m).collect();
for (i, sc) in a.chars().enumerate() {
let mut current = i;
@ -35,10 +40,10 @@ pub fn lev_distance(a: &str, b: &str) -> usize {
dcol[j + 1] = cmp::min(dcol[j + 1], dcol[j]) + 1;
}
current = next;
t_last = j;
}
}
dcol[t_last + 1]
(dcol[m] <= limit).then_some(dcol[m])
}
/// Finds the best match for a given word in the given iterator.
@ -51,39 +56,38 @@ pub fn lev_distance(a: &str, b: &str) -> usize {
/// on an edge case with a lower(upper)case letters mismatch.
#[cold]
pub fn find_best_match_for_name(
name_vec: &[Symbol],
candidates: &[Symbol],
lookup: Symbol,
dist: Option<usize>,
) -> Option<Symbol> {
let lookup = lookup.as_str();
let max_dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3);
let lookup_uppercase = lookup.to_uppercase();
// Priority of matches:
// 1. Exact case insensitive match
// 2. Levenshtein distance match
// 3. Sorted word match
if let Some(case_insensitive_match) =
name_vec.iter().find(|candidate| candidate.as_str().to_uppercase() == lookup.to_uppercase())
{
return Some(*case_insensitive_match);
if let Some(c) = candidates.iter().find(|c| c.as_str().to_uppercase() == lookup_uppercase) {
return Some(*c);
}
let levenshtein_match = name_vec
.iter()
.filter_map(|&name| {
let dist = lev_distance(lookup, name.as_str());
if dist <= max_dist { Some((name, dist)) } else { None }
})
// Here we are collecting the next structure:
// (levenshtein_match, levenshtein_distance)
.fold(None, |result, (candidate, dist)| match result {
None => Some((candidate, dist)),
Some((c, d)) => Some(if dist < d { (candidate, dist) } else { (c, d) }),
});
if levenshtein_match.is_some() {
levenshtein_match.map(|(candidate, _)| candidate)
} else {
find_match_by_sorted_words(name_vec, lookup)
let mut dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3);
let mut best = None;
for c in candidates {
match lev_distance(lookup, c.as_str(), dist) {
Some(0) => return Some(*c),
Some(d) => {
dist = d - 1;
best = Some(*c);
}
None => {}
}
}
if best.is_some() {
return best;
}
find_match_by_sorted_words(candidates, lookup)
}
fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option<Symbol> {

View File

@ -5,18 +5,26 @@ fn test_lev_distance() {
use std::char::{from_u32, MAX};
// Test bytelength agnosticity
for c in (0..MAX as u32).filter_map(from_u32).map(|i| i.to_string()) {
assert_eq!(lev_distance(&c[..], &c[..]), 0);
assert_eq!(lev_distance(&c[..], &c[..], usize::MAX), Some(0));
}
let a = "\nMäry häd ä little lämb\n\nLittle lämb\n";
let b = "\nMary häd ä little lämb\n\nLittle lämb\n";
let c = "Mary häd ä little lämb\n\nLittle lämb\n";
assert_eq!(lev_distance(a, b), 1);
assert_eq!(lev_distance(b, a), 1);
assert_eq!(lev_distance(a, c), 2);
assert_eq!(lev_distance(c, a), 2);
assert_eq!(lev_distance(b, c), 1);
assert_eq!(lev_distance(c, b), 1);
assert_eq!(lev_distance(a, b, usize::MAX), Some(1));
assert_eq!(lev_distance(b, a, usize::MAX), Some(1));
assert_eq!(lev_distance(a, c, usize::MAX), Some(2));
assert_eq!(lev_distance(c, a, usize::MAX), Some(2));
assert_eq!(lev_distance(b, c, usize::MAX), Some(1));
assert_eq!(lev_distance(c, b, usize::MAX), Some(1));
}
#[test]
fn test_lev_distance_limit() {
assert_eq!(lev_distance("abc", "abcd", 1), Some(1));
assert_eq!(lev_distance("abc", "abcd", 0), None);
assert_eq!(lev_distance("abc", "xyz", 3), Some(3));
assert_eq!(lev_distance("abc", "xyz", 2), None);
}
#[test]

View File

@ -15,6 +15,7 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(array_windows)]
#![feature(bool_to_option)]
#![feature(crate_visibility_modifier)]
#![feature(if_let_guard)]
#![feature(negative_impls)]
@ -611,7 +612,7 @@ impl Span {
#[inline]
/// Returns `true` if `hi == lo`.
pub fn is_empty(&self) -> bool {
pub fn is_empty(self) -> bool {
let span = self.data_untracked();
span.hi == span.lo
}
@ -639,7 +640,7 @@ impl Span {
///
/// Use this instead of `==` when either span could be generated code,
/// and you only care that they point to the same bytes of source text.
pub fn source_equal(&self, other: &Span) -> bool {
pub fn source_equal(self, other: Span) -> bool {
let span = self.data();
let other = other.data();
span.lo == other.lo && span.hi == other.hi
@ -680,17 +681,17 @@ impl Span {
}
#[inline]
pub fn rust_2015(&self) -> bool {
pub fn rust_2015(self) -> bool {
self.edition() == edition::Edition::Edition2015
}
#[inline]
pub fn rust_2018(&self) -> bool {
pub fn rust_2018(self) -> bool {
self.edition() >= edition::Edition::Edition2018
}
#[inline]
pub fn rust_2021(&self) -> bool {
pub fn rust_2021(self) -> bool {
self.edition() >= edition::Edition::Edition2021
}
@ -711,7 +712,7 @@ impl Span {
/// Checks if a span is "internal" to a macro in which `#[unstable]`
/// items can be used (that is, a macro marked with
/// `#[allow_internal_unstable]`).
pub fn allows_unstable(&self, feature: Symbol) -> bool {
pub fn allows_unstable(self, feature: Symbol) -> bool {
self.ctxt()
.outer_expn_data()
.allow_internal_unstable
@ -719,7 +720,7 @@ impl Span {
}
/// Checks if this span arises from a compiler desugaring of kind `kind`.
pub fn is_desugaring(&self, kind: DesugaringKind) -> bool {
pub fn is_desugaring(self, kind: DesugaringKind) -> bool {
match self.ctxt().outer_expn_data().kind {
ExpnKind::Desugaring(k) => k == kind,
_ => false,
@ -728,7 +729,7 @@ impl Span {
/// Returns the compiler desugaring that created this span, or `None`
/// if this span is not from a desugaring.
pub fn desugaring_kind(&self) -> Option<DesugaringKind> {
pub fn desugaring_kind(self) -> Option<DesugaringKind> {
match self.ctxt().outer_expn_data().kind {
ExpnKind::Desugaring(k) => Some(k),
_ => None,
@ -738,7 +739,7 @@ impl Span {
/// Checks if a span is "internal" to a macro in which `unsafe`
/// can be used without triggering the `unsafe_code` lint.
// (that is, a macro marked with `#[allow_internal_unsafe]`).
pub fn allows_unsafe(&self) -> bool {
pub fn allows_unsafe(self) -> bool {
self.ctxt().outer_expn_data().allow_internal_unsafe
}
@ -751,7 +752,7 @@ impl Span {
return None;
}
let is_recursive = expn_data.call_site.source_equal(&prev_span);
let is_recursive = expn_data.call_site.source_equal(prev_span);
prev_span = self;
self = expn_data.call_site;
@ -865,13 +866,13 @@ impl Span {
/// Equivalent of `Span::call_site` from the proc macro API,
/// except that the location is taken from the `self` span.
pub fn with_call_site_ctxt(&self, expn_id: ExpnId) -> Span {
pub fn with_call_site_ctxt(self, expn_id: ExpnId) -> Span {
self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
}
/// Equivalent of `Span::mixed_site` from the proc macro API,
/// except that the location is taken from the `self` span.
pub fn with_mixed_site_ctxt(&self, expn_id: ExpnId) -> Span {
pub fn with_mixed_site_ctxt(self, expn_id: ExpnId) -> Span {
self.with_ctxt_from_mark(expn_id, Transparency::SemiTransparent)
}

View File

@ -61,6 +61,15 @@ use rustc_data_structures::fx::FxIndexSet;
/// using the callback `SPAN_TRACK` to access the query engine.
///
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
// FIXME(@lcnr): Enable this attribute once the bootstrap
// compiler knows of `rustc_pass_by_value`.
//
// Right now, this lint would only trigger when compiling the
// stage 2 compiler, which is fairly annoying as there are
// a lot of places using `&Span` right now. After the next bootstrap bump,
// the lint will already trigger when using stage 1, which is a lot less annoying.
//
// #[cfg_attr(not(bootstrap), rustc_pass_by_value)]
pub struct Span {
base_or_index: u32,
len_or_tag: u16,

View File

@ -1904,8 +1904,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
.associated_items(def_id)
.in_definition_order()
.filter(|x| {
let dist = lev_distance(name.as_str(), x.name.as_str());
x.kind.namespace() == Namespace::ValueNS && dist > 0 && dist <= max_dist
if x.kind.namespace() != Namespace::ValueNS {
return false;
}
match lev_distance(name.as_str(), x.name.as_str(), max_dist) {
Some(d) => d > 0,
None => false,
}
})
.copied()
.collect()

View File

@ -515,8 +515,44 @@ pub trait Iterator {
/// assert_eq!((2, 'o'), zipper[2]);
/// ```
///
/// If both iterators have roughly equivalent syntax, it may me more readable to use [`zip`]:
///
/// ```
/// use std::iter::zip;
///
/// let a = [1, 2, 3];
/// let b = [2, 3, 4];
///
/// let mut zipped = zip(
/// a.into_iter().map(|x| x * 2).skip(1),
/// b.into_iter().map(|x| x * 2).skip(1),
/// );
///
/// assert_eq!(zipped.next(), Some((4, 6)));
/// assert_eq!(zipped.next(), Some((6, 8)));
/// assert_eq!(zipped.next(), None);
/// ```
///
/// compared to:
///
/// ```
/// # let a = [1, 2, 3];
/// # let b = [2, 3, 4];
/// #
/// let mut zipped = a
/// .into_iter()
/// .map(|x| x * 2)
/// .skip(1)
/// .zip(b.into_iter().map(|x| x * 2).skip(1));
/// #
/// # assert_eq!(zipped.next(), Some((4, 6)));
/// # assert_eq!(zipped.next(), Some((6, 8)));
/// # assert_eq!(zipped.next(), None);
/// ```
///
/// [`enumerate`]: Iterator::enumerate
/// [`next`]: Iterator::next
/// [`zip`]: crate::iter::zip
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter>

View File

@ -45,94 +45,13 @@ macro_rules! type_alias {
}
}
type_alias! { "char.md", c_char = u8, NonZero_c_char = NonZeroU8;
#[doc(cfg(all()))]
#[cfg(any(
all(
target_os = "linux",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "hexagon",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "riscv64",
target_arch = "riscv32"
)
),
all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")),
all(target_os = "l4re", target_arch = "x86_64"),
all(
target_os = "freebsd",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "riscv64"
)
),
all(
target_os = "netbsd",
any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc")
),
all(target_os = "openbsd", target_arch = "aarch64"),
all(
target_os = "vxworks",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
target_arch = "powerpc"
)
),
all(target_os = "fuchsia", target_arch = "aarch64")
))]}
type_alias! { "char.md", c_char = i8, NonZero_c_char = NonZeroI8;
#[doc(cfg(all()))]
#[cfg(not(any(
all(
target_os = "linux",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "hexagon",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "riscv64",
target_arch = "riscv32"
)
),
all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")),
all(target_os = "l4re", target_arch = "x86_64"),
all(
target_os = "freebsd",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "riscv64"
)
),
all(
target_os = "netbsd",
any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc")
),
all(target_os = "openbsd", target_arch = "aarch64"),
all(
target_os = "vxworks",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
target_arch = "powerpc"
)
),
all(target_os = "fuchsia", target_arch = "aarch64")
)))]}
type_alias! { "char.md", c_char = c_char_definition::c_char, NonZero_c_char = c_char_definition::NonZero_c_char;
// Make this type alias appear cfg-dependent so that Clippy does not suggest
// replacing `0 as c_char` with `0_i8`/`0_u8`. This #[cfg(all())] can be removed
// after the false positive in https://github.com/rust-lang/rust-clippy/issues/8093
// is fixed.
#[cfg(all())]
#[doc(cfg(all()))] }
type_alias! { "schar.md", c_schar = i8, NonZero_c_schar = NonZeroI8; }
type_alias! { "uchar.md", c_uchar = u8, NonZero_c_uchar = NonZeroU8; }
type_alias! { "short.md", c_short = i16, NonZero_c_short = NonZeroI16; }
@ -180,3 +99,58 @@ pub type c_ptrdiff_t = isize;
/// platforms where this is not the case.
#[unstable(feature = "c_size_t", issue = "88345")]
pub type c_ssize_t = isize;
mod c_char_definition {
cfg_if::cfg_if! {
// These are the targets on which c_char is unsigned.
if #[cfg(any(
all(
target_os = "linux",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "hexagon",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "riscv64",
target_arch = "riscv32"
)
),
all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")),
all(target_os = "l4re", target_arch = "x86_64"),
all(
target_os = "freebsd",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "riscv64"
)
),
all(
target_os = "netbsd",
any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc")
),
all(target_os = "openbsd", target_arch = "aarch64"),
all(
target_os = "vxworks",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
target_arch = "powerpc"
)
),
all(target_os = "fuchsia", target_arch = "aarch64")
))] {
pub type c_char = u8;
pub type NonZero_c_char = core::num::NonZeroU8;
} else {
// On every other target, c_char is signed.
pub type c_char = i8;
pub type NonZero_c_char = core::num::NonZeroI8;
}
}
}

View File

@ -0,0 +1,32 @@
// exact-check
const QUERY = [
'Result<SomeTrait>',
'Zzzzzzzzzzzzzzzzzz',
'Nonononononononono',
];
const EXPECTED = [
// check one of the generic items
{
'in_args': [
{ 'path': 'generics_multi_trait', 'name': 'beta' },
],
'returned': [
{ 'path': 'generics_multi_trait', 'name': 'bet' },
],
},
{
'in_args': [
{ 'path': 'generics_multi_trait', 'name': 'beta' },
],
'returned': [
{ 'path': 'generics_multi_trait', 'name': 'bet' },
],
},
// ignore the name of the generic itself
{
'in_args': [],
'returned': [],
},
];

View File

@ -0,0 +1,12 @@
pub trait SomeTrait {}
pub trait Zzzzzzzzzzzzzzzzzz {}
pub fn bet<Nonononononononono: SomeTrait + Zzzzzzzzzzzzzzzzzz>() -> Result<Nonononononononono, ()> {
loop {}
}
pub fn beta<Nonononononononono: SomeTrait + Zzzzzzzzzzzzzzzzzz>(
_param: Result<Nonononononononono, ()>,
) {
loop {}
}

View File

@ -2,7 +2,7 @@ error: usage of qualified `ty::Ty<'_>`
--> $DIR/qualified_ty_ty_ctxt.rs:25:11
|
LL | ty_q: ty::Ty<'_>,
| ^^^^^^^^^^ help: try using it unqualified: `Ty<'_>`
| ^^^^^^^^^^ help: try importing it and using it unqualified: `Ty<'_>`
|
note: the lint level is defined here
--> $DIR/qualified_ty_ty_ctxt.rs:4:9
@ -14,7 +14,7 @@ error: usage of qualified `ty::TyCtxt<'_>`
--> $DIR/qualified_ty_ty_ctxt.rs:27:16
|
LL | ty_ctxt_q: ty::TyCtxt<'_>,
| ^^^^^^^^^^^^^^ help: try using it unqualified: `TyCtxt<'_>`
| ^^^^^^^^^^^^^^ help: try importing it and using it unqualified: `TyCtxt<'_>`
error: aborting due to 2 previous errors