Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d81cc68
fix: add EII function aliases to exported symbols
AsakuraMizu Mar 11, 2026
fd45124
add generic & unresolved inference variables handling in `select_tran…
zedddie Mar 12, 2026
69d9576
refactor: move doc(rust_logo) check to parser
mehdiakiki Mar 17, 2026
48987fd
refactor: reuse doc attr helper for rust_logo
mehdiakiki Mar 18, 2026
c461182
Lex lifetimes using emoji and emit appropriate error
estebank Mar 15, 2026
dfdc525
Unify lifetime and identifier parsing
estebank Mar 18, 2026
702197a
Use `bad_unicode_identifiers` for lifetimes
estebank Mar 18, 2026
ea749ea
Fix rust-analyzer
estebank Mar 18, 2026
956917d
remove whitespace
estebank Mar 18, 2026
4f1676d
fix test
estebank Mar 18, 2026
d29c489
add self-referential param-env normalization regression
TaKO8Ki Mar 8, 2026
6518de3
Skip suggestions pointing to extern macro def for assert_eq
chenyukang Mar 30, 2026
59fe28d
compiler-builtins: Clean up features
tgross35 Jun 18, 2025
0c8e23a
Rollup merge of #142659 - tgross35:builtins-features, r=Amanieu
tgross35 Mar 30, 2026
0712a12
Rollup merge of #153574 - TaKO8Ki:self-referential-param-env-normaliz…
tgross35 Mar 30, 2026
9f1223c
Rollup merge of #153648 - AsakuraMizu:fix-eii-lto-alias, r=jdonszelma…
tgross35 Mar 30, 2026
d53dee3
Rollup merge of #153790 - zedddie:transmutation-regression, r=jdonsze…
tgross35 Mar 30, 2026
8ea2ab7
Rollup merge of #153893 - estebank:emoji-lifetime, r=jdonszelmann,Vey…
tgross35 Mar 30, 2026
f5f76ab
Rollup merge of #153980 - mehdiakiki:pr-check_attrs-rust_logo, r=jdon…
tgross35 Mar 30, 2026
a28a275
Rollup merge of #154551 - chenyukang:yukang-fix-146204-assert-ne-sugg…
tgross35 Mar 30, 2026
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: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4451,6 +4451,7 @@ dependencies = [
"thin-vec",
"tracing",
"unicode-normalization",
"unicode-properties",
"unicode-width 0.2.2",
]

Expand Down
24 changes: 20 additions & 4 deletions compiler/rustc_attr_parsing/src/attributes/doc.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit};
use rustc_errors::msg;
use rustc_feature::template;
use rustc_hir::Target;
use rustc_hir::attrs::{
AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow,
};
use rustc_hir::lints::AttributeLintKind;
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, edition, sym};
use thin_vec::ThinVec;

Expand Down Expand Up @@ -481,15 +483,19 @@ impl DocParser {
}
macro_rules! no_args_and_crate_level {
($ident: ident) => {{
no_args_and_crate_level!($ident, |span| {});
}};
($ident: ident, |$span:ident| $extra_validation:block) => {{
if let Err(span) = args.no_args() {
expected_no_args(cx, span);
return;
}
let span = path.span();
if !check_attr_crate_level(cx, span) {
let $span = path.span();
if !check_attr_crate_level(cx, $span) {
return;
}
self.attribute.$ident = Some(span);
$extra_validation
self.attribute.$ident = Some($span);
}};
}
macro_rules! string_arg_and_crate_level {
Expand Down Expand Up @@ -553,7 +559,17 @@ impl DocParser {
),
Some(sym::fake_variadic) => no_args_and_not_crate_level!(fake_variadic),
Some(sym::search_unbox) => no_args_and_not_crate_level!(search_unbox),
Some(sym::rust_logo) => no_args_and_crate_level!(rust_logo),
Some(sym::rust_logo) => no_args_and_crate_level!(rust_logo, |span| {
if !cx.features().rustdoc_internals() {
feature_err(
cx.sess(),
sym::rustdoc_internals,
span,
msg!("the `#[doc(rust_logo)]` attribute is used for Rust branding"),
)
.emit();
}
}),
Some(sym::auto_cfg) => self.parse_auto_cfg(cx, path, args),
Some(sym::test) => {
let Some(list) = args.list() else {
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,14 @@ fn exported_non_generic_symbols_provider_local<'tcx>(
}))
}

symbols.extend(sorted.iter().flat_map(|&(&def_id, &info)| {
tcx.codegen_fn_attrs(def_id).foreign_item_symbol_aliases.iter().map(
move |&(foreign_item, _linkage, _visibility)| {
(ExportedSymbol::NonGeneric(foreign_item), info)
},
)
}));

if tcx.entry_fn(()).is_some() {
let exported_symbol =
ExportedSymbol::NoDefId(SymbolName::new(tcx, tcx.sess.target.entry_name.as_ref()));
Expand Down
35 changes: 20 additions & 15 deletions compiler/rustc_lexer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ pub enum TokenKind {

/// A lifetime, e.g. `'a`.
Lifetime {
starts_with_number: bool,
invalid: bool,
},

/// `;`
Expand Down Expand Up @@ -584,7 +584,7 @@ impl<'a> Cursor<'a> {
let kind = RawStr { n_hashes: res.ok() };
Literal { kind, suffix_start }
}
_ => self.ident_or_unknown_prefix(),
_ => self.ident_or_unknown_prefix(false),
},

// Byte literal, byte string literal, raw byte string literal or identifier.
Expand All @@ -603,7 +603,7 @@ impl<'a> Cursor<'a> {

// Identifier (this should be checked after other variant that can
// start as identifier).
c if is_id_start(c) => self.ident_or_unknown_prefix(),
c if is_id_start(c) => self.ident_or_unknown_prefix(false),

// Numeric literal.
c @ '0'..='9' => {
Expand Down Expand Up @@ -661,7 +661,7 @@ impl<'a> Cursor<'a> {
Literal { kind, suffix_start }
}
// Identifier starting with an emoji. Only lexed for graceful error recovery.
c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(),
c if is_emoji(c) => self.invalid_ident(),
_ => Unknown,
};
if matches!(self.frontmatter_allowed, FrontmatterAllowed::Yes)
Expand Down Expand Up @@ -832,25 +832,22 @@ impl<'a> Cursor<'a> {
RawIdent
}

fn ident_or_unknown_prefix(&mut self) -> TokenKind {
debug_assert!(is_id_start(self.prev()));
fn ident_or_unknown_prefix(&mut self, already_invalid: bool) -> TokenKind {
debug_assert!(is_id_start(self.prev()) || already_invalid);
// Start is already eaten, eat the rest of identifier.
self.eat_while(is_id_continue);
// Known prefixes must have been handled earlier. So if
// we see a prefix here, it is definitely an unknown prefix.
match self.first() {
'#' | '"' | '\'' => UnknownPrefix,
c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(),
c if is_emoji(c) => self.invalid_ident(),
_ => Ident,
}
}

fn invalid_ident(&mut self) -> TokenKind {
// Start is already eaten, eat the rest of identifier.
self.eat_while(|c| {
const ZERO_WIDTH_JOINER: char = '\u{200d}';
is_id_continue(c) || (!c.is_ascii() && c.is_emoji_char()) || c == ZERO_WIDTH_JOINER
});
self.eat_while(|c| is_id_continue(c) || is_emoji(c));
// An invalid identifier followed by '#' or '"' or '\'' could be
// interpreted as an invalid literal prefix. We don't bother doing that
// because the treatment of invalid identifiers and invalid prefixes
Expand Down Expand Up @@ -895,7 +892,7 @@ impl<'a> Cursor<'a> {
let kind = mk_kind_raw(res.ok());
Literal { kind, suffix_start }
}
_ => self.ident_or_unknown_prefix(),
_ => self.ident_or_unknown_prefix(false),
}
}

Expand Down Expand Up @@ -975,14 +972,18 @@ impl<'a> Cursor<'a> {
fn lifetime_or_char(&mut self) -> TokenKind {
debug_assert!(self.prev() == '\'');

let mut invalid = false;
let can_be_a_lifetime = if self.second() == '\'' {
// It's surely not a lifetime.
false
} else {
// If the first symbol is valid for identifier, it can be a lifetime.
// Also check if it's a number for a better error reporting (so '0 will
// be reported as invalid lifetime and not as unterminated char literal).
is_id_start(self.first()) || self.first().is_ascii_digit()
let c = self.first();
invalid |= c.is_ascii_digit();
invalid |= is_emoji(c);
is_id_start(c) || invalid
};

if !can_be_a_lifetime {
Expand Down Expand Up @@ -1012,7 +1013,7 @@ impl<'a> Cursor<'a> {
// First symbol can be a number (which isn't a valid identifier start),
// so skip it without any checks.
self.bump();
self.eat_while(is_id_continue);
invalid |= matches!(self.ident_or_unknown_prefix(invalid), InvalidIdent);

match self.first() {
// Check if after skipping literal contents we've met a closing
Expand All @@ -1024,7 +1025,7 @@ impl<'a> Cursor<'a> {
Literal { kind, suffix_start: self.pos_within_token() }
}
'#' if !starts_with_number => UnknownPrefixLifetime,
_ => Lifetime { starts_with_number },
_ => Lifetime { invalid },
}
}

Expand Down Expand Up @@ -1277,3 +1278,7 @@ impl<'a> Cursor<'a> {
self.eat_while(is_id_continue);
}
}

fn is_emoji(c: char) -> bool {
!c.is_ascii() && c.is_emoji_char()
}
2 changes: 1 addition & 1 deletion compiler/rustc_lexer/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ fn lifetime() {
"'abc",
FrontmatterAllowed::No,
expect![[r#"
Token { kind: Lifetime { starts_with_number: false }, len: 4 }
Token { kind: Lifetime { invalid: false }, len: 4 }
"#]],
);
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_parse/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ rustc_span = { path = "../rustc_span" }
thin-vec = "0.2.12"
tracing = "0.1"
unicode-normalization = "0.1.25"
unicode-properties = { version = "0.1.4", default-features = false, features = ["emoji"] }
unicode-width = "0.2.2"
# tidy-alphabetical-end

Expand Down
39 changes: 32 additions & 7 deletions compiler/rustc_parse/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use rustc_session::lint::builtin::{
use rustc_session::parse::ParseSess;
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
use tracing::debug;
use unicode_properties::emoji::UnicodeEmoji;

use crate::errors;
use crate::lexer::diagnostics::TokenTreeDiagInfo;
Expand Down Expand Up @@ -315,18 +316,42 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
self.lint_literal_unicode_text_flow(symbol, kind, self.mk_sp(start, self.pos), "literal");
token::Literal(token::Lit { kind, symbol, suffix })
}
rustc_lexer::TokenKind::Lifetime { starts_with_number } => {
rustc_lexer::TokenKind::Lifetime { invalid } => {
// Include the leading `'` in the real identifier, for macro
// expansion purposes. See #12512 for the gory details of why
// this is necessary.
let lifetime_name = nfc_normalize(self.str_from(start));
self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));
if starts_with_number {
let span = self.mk_sp(start, self.pos);
self.dcx()
.struct_err("lifetimes cannot start with a number")
.with_span(span)
.stash(span, StashKey::LifetimeIsChar);
let span = self.mk_sp(start, self.pos);
if invalid {
let name = lifetime_name.as_str();
// skip(1) to skip the `'`
let starts_with_number = matches!(
name.chars().skip(1).next(),
Some(c) if c.is_ascii_digit()
);
if name.chars().any(|c| !c.is_ascii() && c.is_emoji_char()) {
self.psess
.bad_unicode_identifiers
.borrow_mut()
.entry(lifetime_name)
.or_default()
.push(span);
}
if starts_with_number {
let mut err = self.dcx()
.struct_err(format!(
"lifetimes cannot start with a number: `{name}`"
))
.with_span(span);
if name.len() > 2 {
// Point at the first lifetime name character.
let start_span = self.mk_sp(start + BytePos(1), start + BytePos(2));
err.span(start_span);
err.span_label(span, "");
}
err.stash(span, StashKey::LifetimeIsChar);
}
}
token::Lifetime(lifetime_name, IdentIsRaw::No)
}
Expand Down
15 changes: 2 additions & 13 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
html_no_source: _,
// already checked in attr_parsing
issue_tracker_base_url: _,
rust_logo,
// already checked in attr_parsing
rust_logo: _,
// allowed anywhere
test_attrs: _,
// already checked in attr_parsing
Expand Down Expand Up @@ -1217,18 +1218,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {

self.check_doc_inline(hir_id, target, inline);

if let Some(span) = rust_logo
&& !self.tcx.features().rustdoc_internals()
{
feature_err(
&self.tcx.sess,
sym::rustdoc_internals,
*span,
msg!("the `#[doc(rust_logo)]` attribute is used for Rust branding"),
)
.emit();
}

if let Some(span) = masked {
self.check_doc_masked(*span, hir_id, target);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2878,6 +2878,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_predicate: ty::PolyTraitPredicate<'tcx>,
root_obligation: &PredicateObligation<'tcx>,
) -> (PredicateObligation<'tcx>, ty::PolyTraitPredicate<'tcx>) {
if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() {
return (obligation.clone(), trait_predicate);
}

let ocx = ObligationCtxt::new(self);
let normalized_predicate = self.tcx.erase_and_anonymize_regions(
self.tcx.instantiate_bound_regions_with_erased(trait_predicate),
Expand Down
Loading
Loading