Skip to content

Rollup of 7 pull requests #144768

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 22 commits into from
Aug 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
293f95f
add regression test for RUST-143222
lolbinarycat Jul 11, 2025
4762694
rustdoc: never try to link to unnamable types
lolbinarycat Jul 12, 2025
94700f8
fix regression test
lolbinarycat Jul 14, 2025
f57283d
rustdoc: actually never link to unnamable types
lolbinarycat Jul 15, 2025
05a62c8
impl items are never unnamable
lolbinarycat Jul 19, 2025
1a515e6
rustdoc-json: Add test for `#[macro_use]` attribute
aDotInTheVoid Jul 30, 2025
a33e084
rustdoc-json: Move `#[macro_export]` from `Other` to it's own variant
aDotInTheVoid Jul 30, 2025
7b667e7
Extend `is_case_difference` to handle digit-letter confusables
xizheyin Jul 30, 2025
37d52ed
add correct dynamic_lib_extension for aix
Jul 31, 2025
eae1d39
chore: Ping Muscraft when annnotate snippets emitter is modified
Muscraft Jul 31, 2025
1f78738
chore: Ping Muscraft when rustc_errors::emitter is modified
Muscraft Jul 31, 2025
8d0b92a
uefi: Use slice equality rather than `memcmp`
tgross35 Jul 31, 2025
daf3534
Use `core` via `rustc-std-workspace-core` in `library/panic*`
tgross35 Jul 30, 2025
42bf044
Remove `std`'s dependency on `compiler-builtins`
tgross35 Jul 30, 2025
5622816
triagebot: Label `compiler-builtins` T-libs
tgross35 Jul 31, 2025
e6b80f3
Rollup merge of #143849 - lolbinarycat:rustdoc-priv-normalize-143222,…
jhpratt Aug 1, 2025
3f5f045
Rollup merge of #144683 - tgross35:builtins-via-std-workspace, r=bjor…
jhpratt Aug 1, 2025
c7ec9bc
Rollup merge of #144691 - xizheyin:suggest-confuse, r=estebank
jhpratt Aug 1, 2025
400b8b6
Rollup merge of #144700 - aDotInTheVoid:macro-rules-for-macro-fools, …
jhpratt Aug 1, 2025
e11bcb6
Rollup merge of #144751 - dalvescb:curtisd/aix_libextension, r=Noratrieb
jhpratt Aug 1, 2025
a69ecb4
Rollup merge of #144757 - Muscraft:triagebot-emitter-pings, r=lqd
jhpratt Aug 1, 2025
74d5b09
Rollup merge of #144759 - tgross35:triagebot-label, r=lqd
jhpratt Aug 1, 2025
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
135 changes: 105 additions & 30 deletions compiler/rustc_errors/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,19 +262,11 @@ pub trait Emitter {
format!("help: {msg}")
} else {
// Show the default suggestion text with the substitution
format!(
"help: {}{}: `{}`",
msg,
if self
.source_map()
.is_some_and(|sm| is_case_difference(sm, snippet, part.span,))
{
" (notice the capitalization)"
} else {
""
},
snippet,
)
let confusion_type = self
.source_map()
.map(|sm| detect_confusion_type(sm, snippet, part.span))
.unwrap_or(ConfusionType::None);
format!("help: {}{}: `{}`", msg, confusion_type.label_text(), snippet,)
};
primary_span.push_span_label(part.span, msg);

Expand Down Expand Up @@ -2031,12 +2023,12 @@ impl HumanEmitter {
buffer.append(0, ": ", Style::HeaderMsg);

let mut msg = vec![(suggestion.msg.to_owned(), Style::NoStyle)];
if suggestions
.iter()
.take(MAX_SUGGESTIONS)
.any(|(_, _, _, only_capitalization)| *only_capitalization)
if let Some(confusion_type) =
suggestions.iter().take(MAX_SUGGESTIONS).find_map(|(_, _, _, confusion_type)| {
if confusion_type.has_confusion() { Some(*confusion_type) } else { None }
})
{
msg.push((" (notice the capitalization difference)".into(), Style::NoStyle));
msg.push((confusion_type.label_text().into(), Style::NoStyle));
}
self.msgs_to_buffer(
&mut buffer,
Expand Down Expand Up @@ -3531,24 +3523,107 @@ pub fn is_different(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
}

/// Whether the original and suggested code are visually similar enough to warrant extra wording.
pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
// FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode.
pub fn detect_confusion_type(sm: &SourceMap, suggested: &str, sp: Span) -> ConfusionType {
let found = match sm.span_to_snippet(sp) {
Ok(snippet) => snippet,
Err(e) => {
warn!(error = ?e, "Invalid span {:?}", sp);
return false;
return ConfusionType::None;
}
};
let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z'];
// All the chars that differ in capitalization are confusable (above):
let confusable = iter::zip(found.chars(), suggested.chars())
.filter(|(f, s)| f != s)
.all(|(f, s)| ascii_confusables.contains(&f) || ascii_confusables.contains(&s));
confusable && found.to_lowercase() == suggested.to_lowercase()
// FIXME: We sometimes suggest the same thing we already have, which is a
// bug, but be defensive against that here.
&& found != suggested

let mut has_case_confusion = false;
let mut has_digit_letter_confusion = false;

if found.len() == suggested.len() {
let mut has_case_diff = false;
let mut has_digit_letter_confusable = false;
let mut has_other_diff = false;

let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z'];

let digit_letter_confusables = [('0', 'O'), ('1', 'l'), ('5', 'S'), ('8', 'B'), ('9', 'g')];

for (f, s) in iter::zip(found.chars(), suggested.chars()) {
if f != s {
if f.to_lowercase().to_string() == s.to_lowercase().to_string() {
// Check for case differences (any character that differs only in case)
if ascii_confusables.contains(&f) || ascii_confusables.contains(&s) {
has_case_diff = true;
} else {
has_other_diff = true;
}
} else if digit_letter_confusables.contains(&(f, s))
|| digit_letter_confusables.contains(&(s, f))
{
// Check for digit-letter confusables (like 0 vs O, 1 vs l, etc.)
has_digit_letter_confusable = true;
} else {
has_other_diff = true;
}
}
}

// If we have case differences and no other differences
if has_case_diff && !has_other_diff && found != suggested {
has_case_confusion = true;
}
if has_digit_letter_confusable && !has_other_diff && found != suggested {
has_digit_letter_confusion = true;
}
}

match (has_case_confusion, has_digit_letter_confusion) {
(true, true) => ConfusionType::Both,
(true, false) => ConfusionType::Case,
(false, true) => ConfusionType::DigitLetter,
(false, false) => ConfusionType::None,
}
}

/// Represents the type of confusion detected between original and suggested code.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConfusionType {
/// No confusion detected
None,
/// Only case differences (e.g., "hello" vs "Hello")
Case,
/// Only digit-letter confusion (e.g., "0" vs "O", "1" vs "l")
DigitLetter,
/// Both case and digit-letter confusion
Both,
}

impl ConfusionType {
/// Returns the appropriate label text for this confusion type.
pub fn label_text(&self) -> &'static str {
match self {
ConfusionType::None => "",
ConfusionType::Case => " (notice the capitalization)",
ConfusionType::DigitLetter => " (notice the digit/letter confusion)",
ConfusionType::Both => " (notice the capitalization and digit/letter confusion)",
}
}

/// Combines two confusion types. If either is `Both`, the result is `Both`.
/// If one is `Case` and the other is `DigitLetter`, the result is `Both`.
/// Otherwise, returns the non-`None` type, or `None` if both are `None`.
pub fn combine(self, other: ConfusionType) -> ConfusionType {
match (self, other) {
(ConfusionType::None, other) => other,
(this, ConfusionType::None) => this,
(ConfusionType::Both, _) | (_, ConfusionType::Both) => ConfusionType::Both,
(ConfusionType::Case, ConfusionType::DigitLetter)
| (ConfusionType::DigitLetter, ConfusionType::Case) => ConfusionType::Both,
(ConfusionType::Case, ConfusionType::Case) => ConfusionType::Case,
(ConfusionType::DigitLetter, ConfusionType::DigitLetter) => ConfusionType::DigitLetter,
}
}

/// Returns true if this confusion type represents any kind of confusion.
pub fn has_confusion(&self) -> bool {
*self != ConfusionType::None
}
}

pub(crate) fn should_show_source_code(
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub use diagnostic_impls::{
IndicateAnonymousLifetime, SingleLabelManySpans,
};
pub use emitter::ColorConfig;
use emitter::{DynEmitter, Emitter, is_case_difference, is_different};
use emitter::{ConfusionType, DynEmitter, Emitter, detect_confusion_type, is_different};
use rustc_data_structures::AtomicRef;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::stable_hasher::StableHasher;
Expand Down Expand Up @@ -308,7 +308,7 @@ impl CodeSuggestion {
pub(crate) fn splice_lines(
&self,
sm: &SourceMap,
) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, bool)> {
) -> Vec<(String, Vec<SubstitutionPart>, 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 @@ -414,14 +414,15 @@ impl CodeSuggestion {
// We need to keep track of the difference between the existing code and the added
// or deleted code in order to point at the correct column *after* substitution.
let mut acc = 0;
let mut only_capitalization = false;
let mut confusion_type = ConfusionType::None;
for part in &mut substitution.parts {
// 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);

only_capitalization |= is_case_difference(sm, &part.snippet, part.span);
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());
if prev_hi.line == cur_lo.line {
let mut count =
Expand Down Expand Up @@ -511,7 +512,7 @@ impl CodeSuggestion {
if highlights.iter().all(|parts| parts.is_empty()) {
None
} else {
Some((buf, substitution.parts, highlights, only_capitalization))
Some((buf, substitution.parts, highlights, confusion_type))
}
})
.collect()
Expand Down
10 changes: 3 additions & 7 deletions library/Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,8 @@ name = "panic_abort"
version = "0.0.0"
dependencies = [
"alloc",
"compiler_builtins",
"core",
"libc",
"rustc-std-workspace-core",
]

[[package]]
Expand All @@ -194,9 +193,8 @@ version = "0.0.0"
dependencies = [
"alloc",
"cfg-if",
"compiler_builtins",
"core",
"libc",
"rustc-std-workspace-core",
"unwind",
]

Expand Down Expand Up @@ -313,7 +311,6 @@ dependencies = [
"addr2line",
"alloc",
"cfg-if",
"compiler_builtins",
"core",
"dlmalloc",
"fortanix-sgx-abi",
Expand Down Expand Up @@ -380,9 +377,8 @@ name = "unwind"
version = "0.0.0"
dependencies = [
"cfg-if",
"compiler_builtins",
"core",
"libc",
"rustc-std-workspace-core",
"unwinding",
]

Expand Down
3 changes: 1 addition & 2 deletions library/panic_abort/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ bench = false
doc = false

[dependencies]
core = { path = "../core" }
compiler_builtins = { path = "../compiler-builtins/compiler-builtins" }
core = { path = "../rustc-std-workspace-core", package = "rustc-std-workspace-core" }

[target.'cfg(target_os = "android")'.dependencies]
libc = { version = "0.2", default-features = false }
Expand Down
5 changes: 2 additions & 3 deletions library/panic_unwind/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ doc = false

[dependencies]
alloc = { path = "../alloc" }
core = { path = "../core" }
unwind = { path = "../unwind" }
compiler_builtins = { path = "../compiler-builtins/compiler-builtins" }
cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
core = { path = "../rustc-std-workspace-core", package = "rustc-std-workspace-core" }
unwind = { path = "../unwind" }

[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
libc = { version = "0.2", default-features = false }
Expand Down
6 changes: 6 additions & 0 deletions library/rustc-std-workspace-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ on crates.io will draw a dependency edge to `libcore`, the version defined in
this repository. That should draw all the dependency edges to ensure Cargo
builds crates successfully!

`rustc-std-workspace-core` also ensures `compiler-builtins` is in the crate
graph. This crate is used by other crates in `library/`, other than `std` and
`alloc`, so the `compiler-builtins` setup only needs to be configured in a
single place. (Otherwise these crates would just need to depend on `core` and
`compiler-builtins` separately.)

Note that crates on crates.io need to depend on this crate with the name `core`
for everything to work correctly. To do that they can use:

Expand Down
1 change: 0 additions & 1 deletion library/std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core", public = true }
compiler_builtins = { path = "../compiler-builtins/compiler-builtins" }
unwind = { path = "../unwind" }
hashbrown = { version = "0.15", default-features = false, features = [
'rustc-dep-of-std',
Expand Down
22 changes: 11 additions & 11 deletions library/std/src/sys/pal/uefi/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,17 +444,17 @@ impl<'a> DevicePathNode<'a> {

impl<'a> PartialEq for DevicePathNode<'a> {
fn eq(&self, other: &Self) -> bool {
let self_len = self.length();
let other_len = other.length();

self_len == other_len
&& unsafe {
compiler_builtins::mem::memcmp(
self.protocol.as_ptr().cast(),
other.protocol.as_ptr().cast(),
usize::from(self_len),
) == 0
}
// Compare as a single buffer rather than by field since it optimizes better.
//
// SAFETY: `Protocol` is followed by a buffer of `length - sizeof::<Protocol>()`. `Protocol`
// has no padding so it is sound to interpret as a slice.
unsafe {
let s1 =
slice::from_raw_parts(self.protocol.as_ptr().cast::<u8>(), self.length().into());
let s2 =
slice::from_raw_parts(other.protocol.as_ptr().cast::<u8>(), other.length().into());
s1 == s2
}
}
}

Expand Down
3 changes: 1 addition & 2 deletions library/unwind/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ bench = false
doc = false

[dependencies]
core = { path = "../core" }
compiler_builtins = { path = "../compiler-builtins/compiler-builtins" }
cfg-if = "1.0"
core = { path = "../rustc-std-workspace-core", package = "rustc-std-workspace-core" }

[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false }
Expand Down
Loading
Loading