Skip to content

Commit 9e47d8a

Browse files
authored
Rollup merge of rust-lang#139345 - smoelius:into-iter-stability, r=lcnr
Extend `QueryStability` to handle `IntoIterator` implementations This PR extends the `rustc::potential_query_instability` lint to check values passed as `IntoIterator` implementations. Full disclosure: I want the lint to warn about this line (please see rust-lang#138871 for why): https://github.com/rust-lang/rust/blob/aa8f0fd7163a2f23aa958faed30c9c2b77b934a5/src/librustdoc/json/mod.rs#L261 However, the lint warns about several other lines as well. Final note: the functions `get_callee_generic_args_and_args` and `get_input_traits_and_projections` were copied directly from [Clippy's source code](https://github.com/rust-lang/rust/blob/4fd8c04da0674af2c51310c9982370bfadfa1b98/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs#L445-L496).
2 parents 1ae7c49 + c3c2c23 commit 9e47d8a

File tree

8 files changed

+122
-52
lines changed

8 files changed

+122
-52
lines changed

compiler/rustc_codegen_ssa/src/target_features.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ fn parse_rust_feature_flag<'a>(
180180
while let Some(new_feature) = new_features.pop() {
181181
if features.insert(new_feature) {
182182
if let Some(implied_features) = inverse_implied_features.get(&new_feature) {
183+
#[allow(rustc::potential_query_instability)]
183184
new_features.extend(implied_features)
184185
}
185186
}

compiler/rustc_errors/src/emitter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::path::Path;
1717
use std::sync::Arc;
1818

1919
use derive_setters::Setters;
20-
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
20+
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
2121
use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
2222
use rustc_error_messages::{FluentArgs, SpanLabel};
2323
use rustc_lexer;
@@ -1853,7 +1853,7 @@ impl HumanEmitter {
18531853
&& line_idx + 1 == annotated_file.lines.len(),
18541854
);
18551855

1856-
let mut to_add = FxHashMap::default();
1856+
let mut to_add = FxIndexMap::default();
18571857

18581858
for (depth, style) in depths {
18591859
// FIXME(#120456) - is `swap_remove` correct?

compiler/rustc_expand/src/mbe/macro_rules.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
522522
match result {
523523
Success(body_named_matches) => {
524524
psess.gated_spans.merge(gated_spans_snapshot);
525+
#[allow(rustc::potential_query_instability)]
525526
named_matches.extend(body_named_matches);
526527
return Ok((i, rule, named_matches));
527528
}

compiler/rustc_interface/src/interface.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
285285
.expecteds
286286
.entry(name.name)
287287
.and_modify(|v| match v {
288-
ExpectedValues::Some(v) if !values_any_specified => {
288+
ExpectedValues::Some(v) if !values_any_specified =>
289+
{
290+
#[allow(rustc::potential_query_instability)]
289291
v.extend(values.clone())
290292
}
291293
ExpectedValues::Some(_) => *v = ExpectedValues::Any,

compiler/rustc_lint/src/internal.rs

Lines changed: 85 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
22
//! Clippy.
33
4-
use rustc_hir::HirId;
54
use rustc_hir::def::Res;
65
use rustc_hir::def_id::DefId;
7-
use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
6+
use rustc_hir::{Expr, ExprKind, HirId};
7+
use rustc_middle::ty::{self, ClauseKind, GenericArgsRef, PredicatePolarity, TraitPredicate, Ty};
88
use rustc_session::{declare_lint_pass, declare_tool_lint};
99
use rustc_span::hygiene::{ExpnKind, MacroKind};
1010
use rustc_span::{Span, sym};
@@ -56,25 +56,6 @@ impl LateLintPass<'_> for DefaultHashTypes {
5656
}
5757
}
5858

59-
/// Helper function for lints that check for expressions with calls and use typeck results to
60-
/// get the `DefId` and `GenericArgsRef` of the function.
61-
fn typeck_results_of_method_fn<'tcx>(
62-
cx: &LateContext<'tcx>,
63-
expr: &hir::Expr<'_>,
64-
) -> Option<(Span, DefId, ty::GenericArgsRef<'tcx>)> {
65-
match expr.kind {
66-
hir::ExprKind::MethodCall(segment, ..)
67-
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
68-
{
69-
Some((segment.ident.span, def_id, cx.typeck_results().node_args(expr.hir_id)))
70-
}
71-
_ => match cx.typeck_results().node_type(expr.hir_id).kind() {
72-
&ty::FnDef(def_id, args) => Some((expr.span, def_id, args)),
73-
_ => None,
74-
},
75-
}
76-
}
77-
7859
declare_tool_lint! {
7960
/// The `potential_query_instability` lint detects use of methods which can lead to
8061
/// potential query instability, such as iterating over a `HashMap`.
@@ -101,10 +82,12 @@ declare_tool_lint! {
10182

10283
declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]);
10384

104-
impl LateLintPass<'_> for QueryStability {
105-
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
106-
let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return };
107-
if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args)
85+
impl<'tcx> LateLintPass<'tcx> for QueryStability {
86+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
87+
if let Some((callee_def_id, span, generic_args, _recv, _args)) =
88+
get_callee_span_generic_args_and_args(cx, expr)
89+
&& let Ok(Some(instance)) =
90+
ty::Instance::try_resolve(cx.tcx, cx.typing_env(), callee_def_id, generic_args)
10891
{
10992
let def_id = instance.def_id();
11093
if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
@@ -113,7 +96,15 @@ impl LateLintPass<'_> for QueryStability {
11396
span,
11497
QueryInstability { query: cx.tcx.item_name(def_id) },
11598
);
99+
} else if has_unstable_into_iter_predicate(cx, callee_def_id, generic_args) {
100+
let call_span = span.with_hi(expr.span.hi());
101+
cx.emit_span_lint(
102+
POTENTIAL_QUERY_INSTABILITY,
103+
call_span,
104+
QueryInstability { query: sym::into_iter },
105+
);
116106
}
107+
117108
if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) {
118109
cx.emit_span_lint(
119110
UNTRACKED_QUERY_INFORMATION,
@@ -125,6 +116,64 @@ impl LateLintPass<'_> for QueryStability {
125116
}
126117
}
127118

119+
fn has_unstable_into_iter_predicate<'tcx>(
120+
cx: &LateContext<'tcx>,
121+
callee_def_id: DefId,
122+
generic_args: GenericArgsRef<'tcx>,
123+
) -> bool {
124+
let Some(into_iterator_def_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else {
125+
return false;
126+
};
127+
let Some(into_iter_fn_def_id) = cx.tcx.lang_items().into_iter_fn() else {
128+
return false;
129+
};
130+
let predicates = cx.tcx.predicates_of(callee_def_id).instantiate(cx.tcx, generic_args);
131+
for (predicate, _) in predicates {
132+
let ClauseKind::Trait(TraitPredicate { trait_ref, polarity: PredicatePolarity::Positive }) =
133+
predicate.kind().skip_binder()
134+
else {
135+
continue;
136+
};
137+
// Does the function or method require any of its arguments to implement `IntoIterator`?
138+
if trait_ref.def_id != into_iterator_def_id {
139+
continue;
140+
}
141+
let Ok(Some(instance)) =
142+
ty::Instance::try_resolve(cx.tcx, cx.typing_env(), into_iter_fn_def_id, trait_ref.args)
143+
else {
144+
continue;
145+
};
146+
// Does the input type's `IntoIterator` implementation have the
147+
// `rustc_lint_query_instability` attribute on its `into_iter` method?
148+
if cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_query_instability) {
149+
return true;
150+
}
151+
}
152+
false
153+
}
154+
155+
/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
156+
/// `Span`, `GenericArgs`, and arguments. This is a slight augmentation of a similarly named Clippy
157+
/// function, `get_callee_generic_args_and_args`.
158+
fn get_callee_span_generic_args_and_args<'tcx>(
159+
cx: &LateContext<'tcx>,
160+
expr: &'tcx Expr<'tcx>,
161+
) -> Option<(DefId, Span, GenericArgsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
162+
if let ExprKind::Call(callee, args) = expr.kind
163+
&& let callee_ty = cx.typeck_results().expr_ty(callee)
164+
&& let ty::FnDef(callee_def_id, generic_args) = callee_ty.kind()
165+
{
166+
return Some((*callee_def_id, callee.span, generic_args, None, args));
167+
}
168+
if let ExprKind::MethodCall(segment, recv, args, _) = expr.kind
169+
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
170+
{
171+
let generic_args = cx.typeck_results().node_args(expr.hir_id);
172+
return Some((method_def_id, segment.ident.span, generic_args, Some(recv), args));
173+
}
174+
None
175+
}
176+
128177
declare_tool_lint! {
129178
/// The `usage_of_ty_tykind` lint detects usages of `ty::TyKind::<kind>`,
130179
/// where `ty::<kind>` would suffice.
@@ -461,33 +510,22 @@ declare_tool_lint! {
461510
declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);
462511

463512
impl LateLintPass<'_> for Diagnostics {
464-
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
513+
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
465514
let collect_args_tys_and_spans = |args: &[hir::Expr<'_>], reserve_one_extra: bool| {
466515
let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra));
467516
result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span)));
468517
result
469518
};
470519
// Only check function calls and method calls.
471-
let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind {
472-
hir::ExprKind::Call(callee, args) => {
473-
match cx.typeck_results().node_type(callee.hir_id).kind() {
474-
&ty::FnDef(def_id, fn_gen_args) => {
475-
(callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false))
476-
}
477-
_ => return, // occurs for fns passed as args
478-
}
479-
}
480-
hir::ExprKind::MethodCall(_segment, _recv, args, _span) => {
481-
let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr)
482-
else {
483-
return;
484-
};
485-
let mut args = collect_args_tys_and_spans(args, true);
486-
args.insert(0, (cx.tcx.types.self_param, _recv.span)); // dummy inserted for `self`
487-
(span, def_id, fn_gen_args, args)
488-
}
489-
_ => return,
520+
let Some((def_id, span, fn_gen_args, recv, args)) =
521+
get_callee_span_generic_args_and_args(cx, expr)
522+
else {
523+
return;
490524
};
525+
let mut arg_tys_and_spans = collect_args_tys_and_spans(args, recv.is_some());
526+
if let Some(recv) = recv {
527+
arg_tys_and_spans.insert(0, (cx.tcx.types.self_param, recv.span)); // dummy inserted for `self`
528+
}
491529

492530
Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args);
493531
Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans);
@@ -496,7 +534,7 @@ impl LateLintPass<'_> for Diagnostics {
496534

497535
impl Diagnostics {
498536
// Is the type `{D,Subd}iagMessage`?
499-
fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool {
537+
fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: Ty<'cx>) -> bool {
500538
if let Some(adt_def) = ty.ty_adt_def()
501539
&& let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
502540
&& matches!(name, sym::DiagMessage | sym::SubdiagMessage)
@@ -510,7 +548,7 @@ impl Diagnostics {
510548
fn untranslatable_diagnostic<'cx>(
511549
cx: &LateContext<'cx>,
512550
def_id: DefId,
513-
arg_tys_and_spans: &[(MiddleTy<'cx>, Span)],
551+
arg_tys_and_spans: &[(Ty<'cx>, Span)],
514552
) {
515553
let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
516554
let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates;

src/librustdoc/formats/cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub(crate) struct Cache {
4848

4949
/// Similar to `paths`, but only holds external paths. This is only used for
5050
/// generating explicit hyperlinks to other crates.
51-
pub(crate) external_paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>,
51+
pub(crate) external_paths: FxIndexMap<DefId, (Vec<Symbol>, ItemType)>,
5252

5353
/// Maps local `DefId`s of exported types to fully qualified paths.
5454
/// Unlike 'paths', this mapping ignores any renames that occur

tests/ui-fulldeps/internal-lints/query_stability.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,16 @@ fn main() {
3434
//~^ ERROR using `values_mut` can result in unstable query results
3535
*val = *val + 10;
3636
}
37+
38+
FxHashMap::<u32, i32>::default().extend(x);
39+
//~^ ERROR using `into_iter` can result in unstable query results
40+
}
41+
42+
fn hide_into_iter<T>(x: impl IntoIterator<Item = T>) -> impl Iterator<Item = T> {
43+
x.into_iter()
44+
}
45+
46+
fn take(map: std::collections::HashMap<i32, i32>) {
47+
_ = hide_into_iter(map);
48+
//~^ ERROR using `into_iter` can result in unstable query results
3749
}

tests/ui-fulldeps/internal-lints/query_stability.stderr

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,21 @@ LL | for val in x.values_mut() {
5959
|
6060
= note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale
6161

62-
error: aborting due to 7 previous errors
62+
error: using `into_iter` can result in unstable query results
63+
--> $DIR/query_stability.rs:38:38
64+
|
65+
LL | FxHashMap::<u32, i32>::default().extend(x);
66+
| ^^^^^^^^^
67+
|
68+
= note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale
69+
70+
error: using `into_iter` can result in unstable query results
71+
--> $DIR/query_stability.rs:47:9
72+
|
73+
LL | _ = hide_into_iter(map);
74+
| ^^^^^^^^^^^^^^^^^^^
75+
|
76+
= note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale
77+
78+
error: aborting due to 9 previous errors
6379

0 commit comments

Comments
 (0)