Skip to content

Commit 9be2f35

Browse files
committed
Auto merge of rust-lang#103431 - Dylan-DPC:rollup-oozfo89, r=Dylan-DPC
Rollup of 6 pull requests Successful merges: - rust-lang#101293 (Recover when unclosed char literal is parsed as a lifetime in some positions) - rust-lang#101908 (Suggest let for assignment, and some code refactor) - rust-lang#103192 (rustdoc: Eliminate uses of `EarlyDocLinkResolver::all_traits`) - rust-lang#103226 (Check `needs_infer` before `needs_drop` during HIR generator analysis) - rust-lang#103249 (resolve: Revert "Set effective visibilities for imports more precisely") - rust-lang#103305 (Move some tests to more reasonable places) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
2 parents e64f111 + d35a24a commit 9be2f35

36 files changed

+541
-264
lines changed

compiler/rustc_errors/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,9 @@ pub enum StashKey {
463463
UnderscoreForArrayLengths,
464464
EarlySyntaxWarning,
465465
CallIntoMethod,
466+
/// When an invalid lifetime e.g. `'2` should be reinterpreted
467+
/// as a char literal in the parser
468+
LifetimeIsChar,
466469
}
467470

468471
fn default_track_diagnostic(_: &Diagnostic) {}

compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ use crate::{
66
use hir::{def_id::DefId, Body, HirId, HirIdMap};
77
use rustc_data_structures::fx::FxHashSet;
88
use rustc_hir as hir;
9-
use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind};
109
use rustc_middle::ty::{ParamEnv, TyCtxt};
10+
use rustc_middle::{
11+
hir::place::{PlaceBase, Projection, ProjectionKind},
12+
ty::TypeVisitable,
13+
};
1114

1215
pub(super) fn find_consumed_and_borrowed<'a, 'tcx>(
1316
fcx: &'a FnCtxt<'a, 'tcx>,
@@ -198,11 +201,13 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
198201

199202
// If the type being assigned needs dropped, then the mutation counts as a borrow
200203
// since it is essentially doing `Drop::drop(&mut x); x = new_value;`.
201-
//
202-
// FIXME(drop-tracking): We need to be more responsible about inference
203-
// variables here, since `needs_drop` is a "raw" type query, i.e. it
204-
// basically requires types to have been fully resolved.
205-
if assignee_place.place.base_ty.needs_drop(self.tcx, self.param_env) {
204+
let ty = self.tcx.erase_regions(assignee_place.place.base_ty);
205+
if ty.needs_infer() {
206+
self.tcx.sess.delay_span_bug(
207+
self.tcx.hir().span(assignee_place.hir_id),
208+
&format!("inference variables in {ty}"),
209+
);
210+
} else if ty.needs_drop(self.tcx, self.param_env) {
206211
self.places
207212
.borrowed
208213
.insert(TrackedValue::from_place_with_projections_allowed(assignee_place));

compiler/rustc_hir_typeck/src/generator_interior/mod.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -377,15 +377,6 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
377377
debug!("is_borrowed_temporary: {:?}", self.drop_ranges.is_borrowed_temporary(expr));
378378

379379
let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr);
380-
let may_need_drop = |ty: Ty<'tcx>| {
381-
// Avoid ICEs in needs_drop.
382-
let ty = self.fcx.resolve_vars_if_possible(ty);
383-
let ty = self.fcx.tcx.erase_regions(ty);
384-
if ty.needs_infer() {
385-
return true;
386-
}
387-
ty.needs_drop(self.fcx.tcx, self.fcx.param_env)
388-
};
389380

390381
// Typically, the value produced by an expression is consumed by its parent in some way,
391382
// so we only have to check if the parent contains a yield (note that the parent may, for
@@ -403,9 +394,18 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
403394
// src/test/ui/generator/drop-tracking-parent-expression.rs.
404395
let scope = if self.drop_ranges.is_borrowed_temporary(expr)
405396
|| ty.map_or(true, |ty| {
406-
let needs_drop = may_need_drop(ty);
407-
debug!(?needs_drop, ?ty);
408-
needs_drop
397+
// Avoid ICEs in needs_drop.
398+
let ty = self.fcx.resolve_vars_if_possible(ty);
399+
let ty = self.fcx.tcx.erase_regions(ty);
400+
if ty.needs_infer() {
401+
self.fcx
402+
.tcx
403+
.sess
404+
.delay_span_bug(expr.span, &format!("inference variables in {ty}"));
405+
true
406+
} else {
407+
ty.needs_drop(self.fcx.tcx, self.fcx.param_env)
408+
}
409409
}) {
410410
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id)
411411
} else {

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

-5
Original file line numberDiff line numberDiff line change
@@ -587,11 +587,6 @@ impl CStore {
587587
self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess)
588588
}
589589

590-
/// Decodes all traits in the crate (for rustdoc).
591-
pub fn traits_in_crate_untracked(&self, cnum: CrateNum) -> impl Iterator<Item = DefId> + '_ {
592-
self.get_crate_data(cnum).get_traits()
593-
}
594-
595590
/// Decodes all trait impls in the crate (for rustdoc).
596591
pub fn trait_impls_in_crate_untracked(
597592
&self,

compiler/rustc_parse/src/lexer/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use rustc_ast::ast::{self, AttrStyle};
33
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
44
use rustc_ast::tokenstream::TokenStream;
55
use rustc_ast::util::unicode::contains_text_flow_control_chars;
6-
use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
6+
use rustc_errors::{
7+
error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult, StashKey,
8+
};
79
use rustc_lexer::unescape::{self, Mode};
810
use rustc_lexer::Cursor;
911
use rustc_lexer::{Base, DocStyle, RawStrError};
@@ -203,7 +205,10 @@ impl<'a> StringReader<'a> {
203205
// this is necessary.
204206
let lifetime_name = self.str_from(start);
205207
if starts_with_number {
206-
self.err_span_(start, self.pos, "lifetimes cannot start with a number");
208+
let span = self.mk_sp(start, self.pos);
209+
let mut diag = self.sess.struct_err("lifetimes cannot start with a number");
210+
diag.set_span(span);
211+
diag.stash(span, StashKey::LifetimeIsChar);
207212
}
208213
let ident = Symbol::intern(lifetime_name);
209214
token::Lifetime(ident)

compiler/rustc_parse/src/parser/expr.rs

+67-9
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty
4242
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
4343
use rustc_ast::{ClosureBinder, StmtKind};
4444
use rustc_ast_pretty::pprust;
45-
use rustc_errors::IntoDiagnostic;
46-
use rustc_errors::{Applicability, Diagnostic, PResult};
45+
use rustc_errors::{
46+
Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult,
47+
StashKey,
48+
};
4749
use rustc_session::errors::ExprParenthesesNeeded;
4850
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
4951
use rustc_session::lint::BuiltinLintDiagnostics;
@@ -1513,11 +1515,11 @@ impl<'a> Parser<'a> {
15131515
/// Parse `'label: $expr`. The label is already parsed.
15141516
fn parse_labeled_expr(
15151517
&mut self,
1516-
label: Label,
1518+
label_: Label,
15171519
mut consume_colon: bool,
15181520
) -> PResult<'a, P<Expr>> {
1519-
let lo = label.ident.span;
1520-
let label = Some(label);
1521+
let lo = label_.ident.span;
1522+
let label = Some(label_);
15211523
let ate_colon = self.eat(&token::Colon);
15221524
let expr = if self.eat_keyword(kw::While) {
15231525
self.parse_while_expr(label, lo)
@@ -1529,6 +1531,19 @@ impl<'a> Parser<'a> {
15291531
|| self.token.is_whole_block()
15301532
{
15311533
self.parse_block_expr(label, lo, BlockCheckMode::Default)
1534+
} else if !ate_colon
1535+
&& (matches!(self.token.kind, token::CloseDelim(_) | token::Comma)
1536+
|| self.token.is_op())
1537+
{
1538+
let lit = self.recover_unclosed_char(label_.ident, |self_| {
1539+
self_.sess.create_err(UnexpectedTokenAfterLabel {
1540+
span: self_.token.span,
1541+
remove_label: None,
1542+
enclose_in_block: None,
1543+
})
1544+
});
1545+
consume_colon = false;
1546+
Ok(self.mk_expr(lo, ExprKind::Lit(lit)))
15321547
} else if !ate_colon
15331548
&& (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))
15341549
{
@@ -1603,6 +1618,39 @@ impl<'a> Parser<'a> {
16031618
Ok(expr)
16041619
}
16051620

1621+
/// Emit an error when a char is parsed as a lifetime because of a missing quote
1622+
pub(super) fn recover_unclosed_char(
1623+
&mut self,
1624+
lifetime: Ident,
1625+
err: impl FnOnce(&mut Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>,
1626+
) -> ast::Lit {
1627+
if let Some(mut diag) =
1628+
self.sess.span_diagnostic.steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar)
1629+
{
1630+
diag.span_suggestion_verbose(
1631+
lifetime.span.shrink_to_hi(),
1632+
"add `'` to close the char literal",
1633+
"'",
1634+
Applicability::MaybeIncorrect,
1635+
)
1636+
.emit();
1637+
} else {
1638+
err(self)
1639+
.span_suggestion_verbose(
1640+
lifetime.span.shrink_to_hi(),
1641+
"add `'` to close the char literal",
1642+
"'",
1643+
Applicability::MaybeIncorrect,
1644+
)
1645+
.emit();
1646+
}
1647+
ast::Lit {
1648+
token_lit: token::Lit::new(token::LitKind::Char, lifetime.name, None),
1649+
kind: ast::LitKind::Char(lifetime.name.as_str().chars().next().unwrap_or('_')),
1650+
span: lifetime.span,
1651+
}
1652+
}
1653+
16061654
/// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.
16071655
fn recover_do_catch(&mut self) -> PResult<'a, P<Expr>> {
16081656
let lo = self.token.span;
@@ -1728,7 +1776,7 @@ impl<'a> Parser<'a> {
17281776
}
17291777

17301778
pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> {
1731-
self.parse_opt_lit().ok_or_else(|| {
1779+
self.parse_opt_lit().ok_or(()).or_else(|()| {
17321780
if let token::Interpolated(inner) = &self.token.kind {
17331781
let expr = match inner.as_ref() {
17341782
token::NtExpr(expr) => Some(expr),
@@ -1740,12 +1788,22 @@ impl<'a> Parser<'a> {
17401788
let mut err = InvalidInterpolatedExpression { span: self.token.span }
17411789
.into_diagnostic(&self.sess.span_diagnostic);
17421790
err.downgrade_to_delayed_bug();
1743-
return err;
1791+
return Err(err);
17441792
}
17451793
}
17461794
}
1747-
let msg = format!("unexpected token: {}", super::token_descr(&self.token));
1748-
self.struct_span_err(self.token.span, &msg)
1795+
let token = self.token.clone();
1796+
let err = |self_: &mut Self| {
1797+
let msg = format!("unexpected token: {}", super::token_descr(&token));
1798+
self_.struct_span_err(token.span, &msg)
1799+
};
1800+
// On an error path, eagerly consider a lifetime to be an unclosed character lit
1801+
if self.token.is_lifetime() {
1802+
let lt = self.expect_lifetime();
1803+
Ok(self.recover_unclosed_char(lt.ident, err))
1804+
} else {
1805+
Err(err(self))
1806+
}
17491807
})
17501808
}
17511809

compiler/rustc_parse/src/parser/pat.rs

+20
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,25 @@ impl<'a> Parser<'a> {
402402
} else {
403403
PatKind::Path(qself, path)
404404
}
405+
} else if matches!(self.token.kind, token::Lifetime(_))
406+
// In pattern position, we're totally fine with using "next token isn't colon"
407+
// as a heuristic. We could probably just always try to recover if it's a lifetime,
408+
// because we never have `'a: label {}` in a pattern position anyways, but it does
409+
// keep us from suggesting something like `let 'a: Ty = ..` => `let 'a': Ty = ..`
410+
&& !self.look_ahead(1, |token| matches!(token.kind, token::Colon))
411+
{
412+
// Recover a `'a` as a `'a'` literal
413+
let lt = self.expect_lifetime();
414+
let lit = self.recover_unclosed_char(lt.ident, |self_| {
415+
let expected = expected.unwrap_or("pattern");
416+
let msg =
417+
format!("expected {}, found {}", expected, super::token_descr(&self_.token));
418+
419+
let mut err = self_.struct_span_err(self_.token.span, &msg);
420+
err.span_label(self_.token.span, format!("expected {}", expected));
421+
err
422+
});
423+
PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit)))
405424
} else {
406425
// Try to parse everything else as literal with optional minus
407426
match self.parse_literal_maybe_minus() {
@@ -799,6 +818,7 @@ impl<'a> Parser<'a> {
799818
|| t.kind == token::Dot // e.g. `.5` for recovery;
800819
|| t.can_begin_literal_maybe_minus() // e.g. `42`.
801820
|| t.is_whole_expr()
821+
|| t.is_lifetime() // recover `'a` instead of `'a'`
802822
})
803823
}
804824

compiler/rustc_resolve/src/access_levels.rs

+17-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use crate::NameBindingKind;
2-
use crate::Resolver;
1+
use crate::{ImportKind, NameBindingKind, Resolver};
32
use rustc_ast::ast;
43
use rustc_ast::visit;
54
use rustc_ast::visit::Visitor;
@@ -45,31 +44,40 @@ impl<'r, 'a> AccessLevelsVisitor<'r, 'a> {
4544
let module = self.r.get_module(module_id.to_def_id()).unwrap();
4645
let resolutions = self.r.resolutions(module);
4746

48-
for (key, name_resolution) in resolutions.borrow().iter() {
47+
for (_, name_resolution) in resolutions.borrow().iter() {
4948
if let Some(mut binding) = name_resolution.borrow().binding() && !binding.is_ambiguity() {
5049
// Set the given binding access level to `AccessLevel::Public` and
5150
// sets the rest of the `use` chain to `AccessLevel::Exported` until
5251
// we hit the actual exported item.
5352

54-
// FIXME: tag and is_public() condition must be deleted,
55-
// but assertion fail occurs in import_id_for_ns
53+
// FIXME: tag and is_public() condition should be removed, but assertions occur.
5654
let tag = if binding.is_import() { AccessLevel::Exported } else { AccessLevel::Public };
5755
if binding.vis.is_public() {
5856
let mut prev_parent_id = module_id;
5957
let mut level = AccessLevel::Public;
6058
while let NameBindingKind::Import { binding: nested_binding, import, .. } =
6159
binding.kind
6260
{
63-
let id = self.r.local_def_id(self.r.import_id_for_ns(import, key.ns));
64-
self.update(
65-
id,
61+
let mut update = |node_id| self.update(
62+
self.r.local_def_id(node_id),
6663
binding.vis.expect_local(),
6764
prev_parent_id,
6865
level,
6966
);
67+
// In theory all the import IDs have individual visibilities and effective
68+
// visibilities, but in practice these IDs go straigth to HIR where all
69+
// their few uses assume that their (effective) visibility applies to the
70+
// whole syntactic `use` item. So we update them all to the maximum value
71+
// among the potential individual effective visibilities. Maybe HIR for
72+
// imports shouldn't use three IDs at all.
73+
update(import.id);
74+
if let ImportKind::Single { additional_ids, .. } = import.kind {
75+
update(additional_ids.0);
76+
update(additional_ids.1);
77+
}
7078

7179
level = AccessLevel::Exported;
72-
prev_parent_id = id;
80+
prev_parent_id = self.r.local_def_id(import.id);
7381
binding = nested_binding;
7482
}
7583
}

compiler/rustc_resolve/src/imports.rs

+1-26
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::diagnostics::{import_candidates, Suggestion};
44
use crate::Determinacy::{self, *};
5-
use crate::Namespace::{self, *};
5+
use crate::Namespace::*;
66
use crate::{module_to_string, names_to_string, ImportSuggestion};
77
use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
88
use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
@@ -371,31 +371,6 @@ impl<'a> Resolver<'a> {
371371
self.used_imports.insert(import.id);
372372
}
373373
}
374-
375-
/// Take primary and additional node IDs from an import and select one that corresponds to the
376-
/// given namespace. The logic must match the corresponding logic from `fn lower_use_tree` that
377-
/// assigns resolutons to IDs.
378-
pub(crate) fn import_id_for_ns(&self, import: &Import<'_>, ns: Namespace) -> NodeId {
379-
if let ImportKind::Single { additional_ids: (id1, id2), .. } = import.kind {
380-
if let Some(resolutions) = self.import_res_map.get(&import.id) {
381-
assert!(resolutions[ns].is_some(), "incorrectly finalized import");
382-
return match ns {
383-
TypeNS => import.id,
384-
ValueNS => match resolutions.type_ns {
385-
Some(_) => id1,
386-
None => import.id,
387-
},
388-
MacroNS => match (resolutions.type_ns, resolutions.value_ns) {
389-
(Some(_), Some(_)) => id2,
390-
(Some(_), None) | (None, Some(_)) => id1,
391-
(None, None) => import.id,
392-
},
393-
};
394-
}
395-
}
396-
397-
import.id
398-
}
399374
}
400375

401376
/// An error that may be transformed into a diagnostic later. Used to combine multiple unresolved

compiler/rustc_resolve/src/late.rs

+8
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,9 @@ struct DiagnosticMetadata<'ast> {
524524
/// Used to detect possible `if let` written without `let` and to provide structured suggestion.
525525
in_if_condition: Option<&'ast Expr>,
526526

527+
/// Used to detect possible new binding written without `let` and to provide structured suggestion.
528+
in_assignment: Option<&'ast Expr>,
529+
527530
/// If we are currently in a trait object definition. Used to point at the bounds when
528531
/// encountering a struct or enum.
529532
current_trait_object: Option<&'ast [ast::GenericBound]>,
@@ -3905,6 +3908,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
39053908
self.resolve_expr(elem, Some(expr));
39063909
self.visit_expr(idx);
39073910
}
3911+
ExprKind::Assign(..) => {
3912+
let old = self.diagnostic_metadata.in_assignment.replace(expr);
3913+
visit::walk_expr(self, expr);
3914+
self.diagnostic_metadata.in_assignment = old;
3915+
}
39083916
_ => {
39093917
visit::walk_expr(self, expr);
39103918
}

0 commit comments

Comments
 (0)