mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 09:14:20 +00:00
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:
commit
1b4109306c
@ -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;
|
||||
|
@ -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(),
|
||||
¯o_def.body.inner_tokens(),
|
||||
true,
|
||||
*sp,
|
||||
sp,
|
||||
);
|
||||
if macro_def.body.need_semicolon() {
|
||||
self.word(";");
|
||||
|
@ -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)
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
});
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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("_"),
|
||||
})
|
||||
|
@ -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,
|
||||
|
@ -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 "),
|
||||
)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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> {
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
32
src/test/rustdoc-js/generics-multi-trait.js
Normal file
32
src/test/rustdoc-js/generics-multi-trait.js
Normal 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': [],
|
||||
},
|
||||
];
|
12
src/test/rustdoc-js/generics-multi-trait.rs
Normal file
12
src/test/rustdoc-js/generics-multi-trait.rs
Normal 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 {}
|
||||
}
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user