Skip to content

Commit 1c04750

Browse files
committed
Auto merge of #141883 - oli-obk:remove-check-mod-loops, r=nnethercote
Remove check_mod_loops query and run the checks per-body instead This analysis is older than my first rustc contribution I believe. It was never querified. Ideally we'd merge it into the analysis happening within typeck anyway (typeck just uses span_delayed_bug instead of erroring), but I didn't want to do that within this PR that also moves things around and subtly changes diagnostic ordering.
2 parents 1677d46 + 7f4093e commit 1c04750

17 files changed

+272
-271
lines changed

compiler/rustc_hir_typeck/messages.ftl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@ hir_typeck_base_expression_double_dot_enable_default_field_values =
1717
add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
1818
hir_typeck_base_expression_double_dot_remove = remove the `..` as all the fields are already present
1919
20+
hir_typeck_break_inside_closure =
21+
`{$name}` inside of a closure
22+
.label = cannot `{$name}` inside of a closure
23+
.closure_label = enclosing closure
24+
25+
hir_typeck_break_inside_coroutine =
26+
`{$name}` inside `{$kind}` {$source}
27+
.label = cannot `{$name}` inside `{$kind}` {$source}
28+
.coroutine_label = enclosing `{$kind}` {$source}
29+
30+
hir_typeck_break_non_loop =
31+
`break` with value from a `{$kind}` loop
32+
.label = can only break with a value inside `loop` or breakable block
33+
.label2 = you can't `break` with a value in a `{$kind}` loop
34+
.suggestion = use `break` on its own without a value inside this `{$kind}` loop
35+
.break_expr_suggestion = alternatively, you might have meant to use the available loop label
36+
37+
2038
hir_typeck_candidate_trait_note = `{$trait_name}` defines an item `{$item_name}`{$action_or_ty ->
2139
[NONE] {""}
2240
[implement] , perhaps you need to implement it
@@ -64,6 +82,12 @@ hir_typeck_const_select_must_be_fn = this argument must be a function item
6482
.note = expected a function item, found {$ty}
6583
.help = consult the documentation on `const_eval_select` for more information
6684
85+
hir_typeck_continue_labeled_block =
86+
`continue` pointing to a labeled block
87+
.label = labeled blocks cannot be `continue`'d
88+
.block_label = labeled block the `continue` points to
89+
90+
6791
hir_typeck_convert_to_str = try converting the passed type into a `&str`
6892
6993
hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to `{$expected}`
@@ -182,6 +206,19 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte
182206
hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}`
183207
hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}`
184208
209+
hir_typeck_outside_loop =
210+
`{$name}` outside of a loop{$is_break ->
211+
[true] {" or labeled block"}
212+
*[false] {""}
213+
}
214+
.label = cannot `{$name}` outside of a loop{$is_break ->
215+
[true] {" or labeled block"}
216+
*[false] {""}
217+
}
218+
219+
hir_typeck_outside_loop_suggestion = consider labeling this block to be able to break within it
220+
221+
185222
hir_typeck_params_not_allowed =
186223
referencing function parameters is not allowed in naked functions
187224
.help = follow the calling convention in asm block to use parameters
@@ -254,6 +291,13 @@ hir_typeck_union_pat_dotdot = `..` cannot be used in union patterns
254291
255292
hir_typeck_union_pat_multiple_fields = union patterns should have exactly one field
256293
294+
hir_typeck_unlabeled_cf_in_while_condition =
295+
`break` or `continue` with no label in the condition of a `while` loop
296+
.label = unlabeled `{$cf_type}` in the condition of a `while` loop
297+
298+
hir_typeck_unlabeled_in_labeled_block =
299+
unlabeled `{$cf_type}` inside of a labeled block
300+
.label = `{$cf_type}` statements that would diverge to or through a labeled block need to bear a label
257301
hir_typeck_use_is_empty =
258302
consider using the `is_empty` method on `{$expr_ty}` to determine if it contains anything
259303

compiler/rustc_hir_typeck/src/errors.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22
33
use std::borrow::Cow;
44

5+
use rustc_ast::Label;
56
use rustc_errors::codes::*;
67
use rustc_errors::{
78
Applicability, Diag, DiagArgValue, DiagCtxtHandle, DiagSymbolList, Diagnostic,
89
EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic,
910
};
11+
use rustc_hir as hir;
12+
use rustc_hir::ExprKind;
1013
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
1114
use rustc_middle::ty::{self, Ty};
1215
use rustc_span::edition::{Edition, LATEST_STABLE_EDITION};
@@ -721,6 +724,131 @@ pub(crate) struct TrivialCast<'tcx> {
721724
pub cast_ty: Ty<'tcx>,
722725
}
723726

727+
pub(crate) struct BreakNonLoop<'a> {
728+
pub span: Span,
729+
pub head: Option<Span>,
730+
pub kind: &'a str,
731+
pub suggestion: String,
732+
pub loop_label: Option<Label>,
733+
pub break_label: Option<Label>,
734+
pub break_expr_kind: &'a ExprKind<'a>,
735+
pub break_expr_span: Span,
736+
}
737+
738+
impl<'a, G: EmissionGuarantee> Diagnostic<'_, G> for BreakNonLoop<'a> {
739+
#[track_caller]
740+
fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
741+
let mut diag = Diag::new(dcx, level, fluent::hir_typeck_break_non_loop);
742+
diag.span(self.span);
743+
diag.code(E0571);
744+
diag.arg("kind", self.kind);
745+
diag.span_label(self.span, fluent::hir_typeck_label);
746+
if let Some(head) = self.head {
747+
diag.span_label(head, fluent::hir_typeck_label2);
748+
}
749+
diag.span_suggestion(
750+
self.span,
751+
fluent::hir_typeck_suggestion,
752+
self.suggestion,
753+
Applicability::MaybeIncorrect,
754+
);
755+
if let (Some(label), None) = (self.loop_label, self.break_label) {
756+
match self.break_expr_kind {
757+
ExprKind::Path(hir::QPath::Resolved(
758+
None,
759+
hir::Path { segments: [segment], res: hir::def::Res::Err, .. },
760+
)) if label.ident.to_string() == format!("'{}", segment.ident) => {
761+
// This error is redundant, we will have already emitted a
762+
// suggestion to use the label when `segment` wasn't found
763+
// (hence the `Res::Err` check).
764+
diag.downgrade_to_delayed_bug();
765+
}
766+
_ => {
767+
diag.span_suggestion(
768+
self.break_expr_span,
769+
fluent::hir_typeck_break_expr_suggestion,
770+
label.ident,
771+
Applicability::MaybeIncorrect,
772+
);
773+
}
774+
}
775+
}
776+
diag
777+
}
778+
}
779+
780+
#[derive(Diagnostic)]
781+
#[diag(hir_typeck_continue_labeled_block, code = E0696)]
782+
pub(crate) struct ContinueLabeledBlock {
783+
#[primary_span]
784+
#[label]
785+
pub span: Span,
786+
#[label(hir_typeck_block_label)]
787+
pub block_span: Span,
788+
}
789+
790+
#[derive(Diagnostic)]
791+
#[diag(hir_typeck_break_inside_closure, code = E0267)]
792+
pub(crate) struct BreakInsideClosure<'a> {
793+
#[primary_span]
794+
#[label]
795+
pub span: Span,
796+
#[label(hir_typeck_closure_label)]
797+
pub closure_span: Span,
798+
pub name: &'a str,
799+
}
800+
801+
#[derive(Diagnostic)]
802+
#[diag(hir_typeck_break_inside_coroutine, code = E0267)]
803+
pub(crate) struct BreakInsideCoroutine<'a> {
804+
#[primary_span]
805+
#[label]
806+
pub span: Span,
807+
#[label(hir_typeck_coroutine_label)]
808+
pub coroutine_span: Span,
809+
pub name: &'a str,
810+
pub kind: &'a str,
811+
pub source: &'a str,
812+
}
813+
814+
#[derive(Diagnostic)]
815+
#[diag(hir_typeck_outside_loop, code = E0268)]
816+
pub(crate) struct OutsideLoop<'a> {
817+
#[primary_span]
818+
#[label]
819+
pub spans: Vec<Span>,
820+
pub name: &'a str,
821+
pub is_break: bool,
822+
#[subdiagnostic]
823+
pub suggestion: Option<OutsideLoopSuggestion>,
824+
}
825+
#[derive(Subdiagnostic)]
826+
#[multipart_suggestion(hir_typeck_outside_loop_suggestion, applicability = "maybe-incorrect")]
827+
pub(crate) struct OutsideLoopSuggestion {
828+
#[suggestion_part(code = "'block: ")]
829+
pub block_span: Span,
830+
#[suggestion_part(code = " 'block")]
831+
pub break_spans: Vec<Span>,
832+
}
833+
834+
#[derive(Diagnostic)]
835+
#[diag(hir_typeck_unlabeled_in_labeled_block, code = E0695)]
836+
pub(crate) struct UnlabeledInLabeledBlock<'a> {
837+
#[primary_span]
838+
#[label]
839+
pub span: Span,
840+
pub cf_type: &'a str,
841+
}
842+
843+
#[derive(Diagnostic)]
844+
#[diag(hir_typeck_unlabeled_cf_in_while_condition, code = E0590)]
845+
pub(crate) struct UnlabeledCfInWhileCondition<'a> {
846+
#[primary_span]
847+
#[label]
848+
pub span: Span,
849+
pub cf_type: &'a str,
850+
}
851+
724852
#[derive(Diagnostic)]
725853
#[diag(hir_typeck_no_associated_item, code = E0599)]
726854
pub(crate) struct NoAssociatedItem {

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mod fallback;
2828
mod fn_ctxt;
2929
mod gather_locals;
3030
mod intrinsicck;
31+
mod loops;
3132
mod method;
3233
mod naked_functions;
3334
mod op;
@@ -149,6 +150,7 @@ fn typeck_with_inspect<'tcx>(
149150
};
150151

151152
check_abi(tcx, id, span, fn_sig.abi());
153+
loops::check(tcx, def_id, body);
152154

153155
// Compute the function signature from point of view of inside the fn.
154156
let mut fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
@@ -189,6 +191,8 @@ fn typeck_with_inspect<'tcx>(
189191
tcx.type_of(def_id).instantiate_identity()
190192
};
191193

194+
loops::check(tcx, def_id, body);
195+
192196
let expected_type = fcx.normalize(body.value.span, expected_type);
193197

194198
let wf_code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(def_id)));

compiler/rustc_passes/src/loops.rs renamed to compiler/rustc_hir_typeck/src/loops.rs

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ use std::fmt;
33

44
use Context::*;
55
use rustc_hir as hir;
6-
use rustc_hir::def_id::{LocalDefId, LocalModDefId};
6+
use rustc_hir::def::DefKind;
7+
use rustc_hir::def_id::LocalDefId;
78
use rustc_hir::intravisit::{self, Visitor};
89
use rustc_hir::{Destination, Node};
910
use rustc_middle::hir::nested_filter;
10-
use rustc_middle::query::Providers;
1111
use rustc_middle::span_bug;
1212
use rustc_middle::ty::TyCtxt;
1313
use rustc_span::hygiene::DesugaringKind;
@@ -73,51 +73,32 @@ struct CheckLoopVisitor<'tcx> {
7373
block_breaks: BTreeMap<Span, BlockInfo>,
7474
}
7575

76-
fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
76+
pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) {
7777
let mut check =
7878
CheckLoopVisitor { tcx, cx_stack: vec![Normal], block_breaks: Default::default() };
79-
tcx.hir_visit_item_likes_in_module(module_def_id, &mut check);
79+
let cx = match tcx.def_kind(def_id) {
80+
DefKind::AnonConst => AnonConst,
81+
_ => Fn,
82+
};
83+
check.with_context(cx, |v| v.visit_body(body));
8084
check.report_outside_loop_error();
8185
}
8286

83-
pub(crate) fn provide(providers: &mut Providers) {
84-
*providers = Providers { check_mod_loops, ..*providers };
85-
}
86-
8787
impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
8888
type NestedFilter = nested_filter::OnlyBodies;
8989

9090
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
9191
self.tcx
9292
}
9393

94-
fn visit_anon_const(&mut self, c: &'hir hir::AnonConst) {
95-
self.with_context(AnonConst, |v| intravisit::walk_anon_const(v, c));
94+
fn visit_anon_const(&mut self, _: &'hir hir::AnonConst) {
95+
// Typecked on its own.
9696
}
9797

9898
fn visit_inline_const(&mut self, c: &'hir hir::ConstBlock) {
9999
self.with_context(ConstBlock, |v| intravisit::walk_inline_const(v, c));
100100
}
101101

102-
fn visit_fn(
103-
&mut self,
104-
fk: hir::intravisit::FnKind<'hir>,
105-
fd: &'hir hir::FnDecl<'hir>,
106-
b: hir::BodyId,
107-
_: Span,
108-
id: LocalDefId,
109-
) {
110-
self.with_context(Fn, |v| intravisit::walk_fn(v, fk, fd, b, id));
111-
}
112-
113-
fn visit_trait_item(&mut self, trait_item: &'hir hir::TraitItem<'hir>) {
114-
self.with_context(Fn, |v| intravisit::walk_trait_item(v, trait_item));
115-
}
116-
117-
fn visit_impl_item(&mut self, impl_item: &'hir hir::ImplItem<'hir>) {
118-
self.with_context(Fn, |v| intravisit::walk_impl_item(v, impl_item));
119-
}
120-
121102
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
122103
match e.kind {
123104
hir::ExprKind::If(cond, then, else_opt) => {

compiler/rustc_interface/src/passes.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,6 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
954954
tcx.ensure_ok().exportable_items(LOCAL_CRATE);
955955
tcx.ensure_ok().stable_order_of_exportable_impls(LOCAL_CRATE);
956956
tcx.par_hir_for_each_module(|module| {
957-
tcx.ensure_ok().check_mod_loops(module);
958957
tcx.ensure_ok().check_mod_attrs(module);
959958
tcx.ensure_ok().check_mod_unstable_api_usage(module);
960959
});

compiler/rustc_middle/src/query/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,11 +1115,6 @@ rustc_queries! {
11151115
desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) }
11161116
}
11171117

1118-
/// Checks the loops in the module.
1119-
query check_mod_loops(key: LocalModDefId) {
1120-
desc { |tcx| "checking loops in {}", describe_as_module(key, tcx) }
1121-
}
1122-
11231118
query check_mod_privacy(key: LocalModDefId) {
11241119
desc { |tcx| "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) }
11251120
}

0 commit comments

Comments
 (0)