Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions compiler/rustc_errors/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2154,11 +2154,11 @@ impl HumanEmitter {

assert!(!file_lines.lines.is_empty() || parts[0].span.is_dummy());

let line_start = sm.lookup_char_pos(parts[0].span.lo()).line;
let line_start = sm.lookup_char_pos(parts[0].original_span.lo()).line;
let mut lines = complete.lines();
if lines.clone().next().is_none() {
// Account for a suggestion to completely remove a line(s) with whitespace (#94192).
let line_end = sm.lookup_char_pos(parts[0].span.hi()).line;
let line_end = sm.lookup_char_pos(parts[0].original_span.hi()).line;
for line in line_start..=line_end {
self.draw_line_num(
&mut buffer,
Expand Down
71 changes: 46 additions & 25 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,13 @@ pub struct SubstitutionPart {
pub snippet: String,
}

#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
pub struct TrimmedSubstitutionPart {
pub original_span: Span,
pub span: Span,
pub snippet: String,
}

/// Used to translate between `Span`s and byte positions within a single output line in highlighted
/// code of structured suggestions.
#[derive(Debug, Clone, Copy)]
Expand All @@ -233,6 +240,35 @@ pub(crate) struct SubstitutionHighlight {
}

impl SubstitutionPart {
/// Try to turn a replacement into an addition when the span that is being
/// overwritten matches either the prefix or suffix of the replacement.
fn trim_trivial_replacements(self, sm: &SourceMap) -> TrimmedSubstitutionPart {
let mut trimmed_part = TrimmedSubstitutionPart {
original_span: self.span,
span: self.span,
snippet: self.snippet,
};
if trimmed_part.snippet.is_empty() {
return trimmed_part;
}
let Ok(snippet) = sm.span_to_snippet(trimmed_part.span) else {
return trimmed_part;
};

if let Some((prefix, substr, suffix)) = as_substr(&snippet, &trimmed_part.snippet) {
trimmed_part.span = Span::new(
trimmed_part.span.lo() + BytePos(prefix as u32),
trimmed_part.span.hi() - BytePos(suffix as u32),
trimmed_part.span.ctxt(),
trimmed_part.span.parent(),
);
trimmed_part.snippet = substr.to_string();
}
trimmed_part
}
}

impl TrimmedSubstitutionPart {
pub fn is_addition(&self, sm: &SourceMap) -> bool {
!self.snippet.is_empty() && !self.replaces_meaningful_content(sm)
}
Expand Down Expand Up @@ -260,27 +296,6 @@ impl SubstitutionPart {
sm.span_to_snippet(self.span)
.map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty())
}

/// Try to turn a replacement into an addition when the span that is being
/// overwritten matches either the prefix or suffix of the replacement.
fn trim_trivial_replacements(&mut self, sm: &SourceMap) {
if self.snippet.is_empty() {
return;
}
let Ok(snippet) = sm.span_to_snippet(self.span) else {
return;
};

if let Some((prefix, substr, suffix)) = as_substr(&snippet, &self.snippet) {
self.span = Span::new(
self.span.lo() + BytePos(prefix as u32),
self.span.hi() - BytePos(suffix as u32),
self.span.ctxt(),
self.span.parent(),
);
self.snippet = substr.to_string();
}
}
}

/// Given an original string like `AACC`, and a suggestion like `AABBCC`, try to detect
Expand Down Expand Up @@ -310,7 +325,8 @@ impl CodeSuggestion {
pub(crate) fn splice_lines(
&self,
sm: &SourceMap,
) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, ConfusionType)> {
) -> Vec<(String, Vec<TrimmedSubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, ConfusionType)>
{
// For the `Vec<Vec<SubstitutionHighlight>>` value, the first level of the vector
// corresponds to the output snippet's lines, while the second level corresponds to the
// substrings within that line that should be highlighted.
Expand Down Expand Up @@ -428,12 +444,17 @@ impl CodeSuggestion {
// or deleted code in order to point at the correct column *after* substitution.
let mut acc = 0;
let mut confusion_type = ConfusionType::None;
for part in &mut substitution.parts {

let trimmed_parts = substitution
.parts
.into_iter()
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
// suggestion and snippet to look as if we just suggested to add
// `"b"`, which is typically much easier for the user to understand.
part.trim_trivial_replacements(sm);
.map(|part| part.trim_trivial_replacements(sm))
.collect::<Vec<_>>();

for part in &trimmed_parts {
let part_confusion = detect_confusion_type(sm, &part.snippet, part.span);
confusion_type = confusion_type.combine(part_confusion);
let cur_lo = sm.lookup_char_pos(part.span.lo());
Expand Down Expand Up @@ -521,7 +542,7 @@ impl CodeSuggestion {
if highlights.iter().all(|parts| parts.is_empty()) {
None
} else {
Some((buf, substitution.parts, highlights, confusion_type))
Some((buf, trimmed_parts, highlights, confusion_type))
}
})
.collect()
Expand Down
40 changes: 40 additions & 0 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2547,6 +2547,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"you must specify a type for this binding, like `{concrete_type}`",
);

// FIXME: Maybe FileName::Anon should also be handled,
// otherwise there would be no suggestion if the source is STDIN for example.
match (filename, parent_node) {
(
FileName::Real(_),
Expand All @@ -2568,6 +2570,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MaybeIncorrect,
);
}
// For closure parameters with reference patterns (e.g., |&v|), suggest the type annotation
// on the pattern itself, e.g., |&v: &i32|
(FileName::Real(_), Node::Pat(pat))
if let Node::Pat(binding_pat) = self.tcx.hir_node(hir_id)
&& let hir::PatKind::Binding(..) = binding_pat.kind
&& let Node::Pat(parent_pat) = parent_node
&& matches!(parent_pat.kind, hir::PatKind::Ref(..)) =>
{
err.span_label(span, "you must specify a type for this binding");

let mut ref_muts = Vec::new();
let mut current_node = parent_node;

while let Node::Pat(parent_pat) = current_node {
if let hir::PatKind::Ref(_, mutability) = parent_pat.kind {
ref_muts.push(mutability);
current_node = self.tcx.parent_hir_node(parent_pat.hir_id);
} else {
break;
}
}

let mut type_annotation = String::new();
for mutability in ref_muts.iter().rev() {
match mutability {
hir::Mutability::Mut => type_annotation.push_str("&mut "),
hir::Mutability::Not => type_annotation.push('&'),
}
}
type_annotation.push_str(&concrete_type);

err.span_suggestion_verbose(
pat.span.shrink_to_hi(),
"specify the type in the closure argument list",
format!(": {type_annotation}"),
Applicability::MaybeIncorrect,
);
}
_ => {
err.span_label(span, msg);
}
Expand Down
33 changes: 14 additions & 19 deletions library/alloc/src/collections/btree/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

use core::marker::PhantomData;
use core::mem::{self, MaybeUninit};
use core::num::NonZero;
use core::ptr::{self, NonNull};
use core::slice::SliceIndex;

Expand Down Expand Up @@ -143,7 +144,7 @@ type BoxedNode<K, V> = NonNull<LeafNode<K, V>>;
///
/// A reference to a node.
///
/// This type has a number of parameters that controls how it acts:
/// This type has a number of parameters that control how it acts:
/// - `BorrowType`: A dummy type that describes the kind of borrow and carries a lifetime.
/// - When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`.
/// - When this is `ValMut<'a>`, the `NodeRef` acts roughly like `&'a Node`
Expand Down Expand Up @@ -226,33 +227,27 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {

fn from_new_leaf<A: Allocator + Clone>(leaf: Box<LeafNode<K, V>, A>) -> Self {
// The allocator must be dropped, not leaked. See also `BTreeMap::alloc`.
let (leaf, _alloc) = Box::into_raw_with_allocator(leaf);
// SAFETY: the node was just allocated.
let node = unsafe { NonNull::new_unchecked(leaf) };
let (node, _alloc) = Box::into_non_null_with_allocator(leaf);
NodeRef { height: 0, node, _marker: PhantomData }
}
}

impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
/// Creates a new internal (height > 0) `NodeRef`
fn new_internal<A: Allocator + Clone>(child: Root<K, V>, alloc: A) -> Self {
let mut new_node = unsafe { InternalNode::new(alloc) };
new_node.edges[0].write(child.node);
unsafe { NodeRef::from_new_internal(new_node, child.height + 1) }
NodeRef::from_new_internal(new_node, NonZero::new(child.height + 1).unwrap())
}

/// # Safety
/// `height` must not be zero.
unsafe fn from_new_internal<A: Allocator + Clone>(
/// Creates a new internal (height > 0) `NodeRef` from an existing internal node
fn from_new_internal<A: Allocator + Clone>(
internal: Box<InternalNode<K, V>, A>,
height: usize,
height: NonZero<usize>,
) -> Self {
debug_assert!(height > 0);
// The allocator must be dropped, not leaked. See also `BTreeMap::alloc`.
let (internal, _alloc) = Box::into_raw_with_allocator(internal);
// SAFETY: the node was just allocated.
let internal = unsafe { NonNull::new_unchecked(internal) };
let node = internal.cast();
let mut this = NodeRef { height, node, _marker: PhantomData };
let (node, _alloc) = Box::into_non_null_with_allocator(internal);
let mut this = NodeRef { height: height.into(), node: node.cast(), _marker: PhantomData };
this.borrow_mut().correct_all_childrens_parent_links();
this
}
Expand Down Expand Up @@ -625,9 +620,8 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
let top = self.node;

// SAFETY: we asserted to be internal.
let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() };
// SAFETY: we borrowed `self` exclusively and its borrow type is exclusive.
let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) };
let mut internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() };
let internal_node = internal_self.as_internal_mut();
// SAFETY: the first edge is always initialized.
self.node = unsafe { internal_node.edges[0].assume_init_read() };
self.height -= 1;
Expand Down Expand Up @@ -1305,7 +1299,8 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>,
&mut new_node.edges[..new_len + 1],
);

let height = self.node.height;
// SAFETY: self is `marker::Internal`, so `self.node.height` is positive
let height = NonZero::new_unchecked(self.node.height);
let right = NodeRef::from_new_internal(new_node, height);

SplitResult { left: self.node, kv, right }
Expand Down
Loading
Loading