This commit is contained in:
Samuel Moelius 2025-04-13 13:26:26 +02:00 committed by GitHub
commit 3ff09b8635
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 144 additions and 11 deletions

View File

@ -17,7 +17,7 @@ use std::path::Path;
use std::sync::Arc;
use derive_setters::Setters;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
use rustc_error_messages::{FluentArgs, SpanLabel};
use rustc_lexer;
@ -1840,7 +1840,7 @@ impl HumanEmitter {
!is_cont && line_idx + 1 == annotated_file.lines.len(),
);
let mut to_add = FxHashMap::default();
let mut to_add = FxIndexMap::default();
for (depth, style) in depths {
// FIXME(#120456) - is `swap_remove` correct?

View File

@ -283,7 +283,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
.expecteds
.entry(name.name)
.and_modify(|v| match v {
ExpectedValues::Some(v) if !values_any_specified => {
ExpectedValues::Some(v) if !values_any_specified =>
{
#[allow(rustc::potential_query_instability)]
v.extend(values.clone())
}
ExpectedValues::Some(_) => *v = ExpectedValues::Any,

View File

@ -1,10 +1,12 @@
//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy.
use rustc_hir::HirId;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
use rustc_hir::{Expr, ExprKind, HirId};
use rustc_middle::ty::{
self, ClauseKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty as MiddleTy,
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::{Span, sym};
@ -101,10 +103,11 @@ declare_tool_lint! {
declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]);
impl LateLintPass<'_> for QueryStability {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return };
if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args)
impl<'tcx> LateLintPass<'tcx> for QueryStability {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr)
&& let Ok(Some(instance)) =
ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args)
{
let def_id = instance.def_id();
if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
@ -122,9 +125,124 @@ impl LateLintPass<'_> for QueryStability {
);
}
}
check_into_iter_stability(cx, expr);
}
}
fn check_into_iter_stability<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
let Some(into_iterator_def_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else {
return;
};
if expr.span.from_expansion() {
return;
};
// Is `expr` a function or method call?
let Some((callee_def_id, generic_args, recv, args)) =
get_callee_generic_args_and_args(cx, expr)
else {
return;
};
let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder();
let (_, inputs) = fn_sig.inputs_and_output.as_slice().split_last().unwrap();
for (arg_index, &input) in inputs.iter().enumerate() {
let &ty::Param(ParamTy { index: param_index, .. }) = input.kind() else {
continue;
};
let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input);
for TraitPredicate { trait_ref, .. } in trait_predicates {
// Does the function or method require any of its arguments to implement `IntoIterator`?
if trait_ref.def_id != into_iterator_def_id {
continue;
}
let self_ty = generic_args[param_index as usize].expect_ty();
let Some(self_ty_adt_def) = self_ty.peel_refs().ty_adt_def() else {
return;
};
cx.tcx.for_each_relevant_impl(into_iterator_def_id, self_ty, |impl_id| {
let impl_ty = cx.tcx.type_of(impl_id).skip_binder();
let Some(impl_ty_adt_def) = impl_ty.peel_refs().ty_adt_def() else {
return;
};
// To reduce false positives, verify that `self_ty` and `impl_ty` refer to the same ADT.
if self_ty_adt_def != impl_ty_adt_def {
return;
}
let Some(into_iter_item) = cx
.tcx
.associated_items(impl_id)
.filter_by_name_unhygienic(sym::into_iter)
.next()
else {
return;
};
// Does the input type's `IntoIterator` implementation have the
// `rustc_lint_query_instability` attribute on its `into_iter` method?
if !cx.tcx.has_attr(into_iter_item.def_id, sym::rustc_lint_query_instability) {
return;
}
let span = if let Some(recv) = recv {
if arg_index == 0 { recv.span } else { args[arg_index - 1].span }
} else {
args[arg_index].span
};
cx.emit_span_lint(
POTENTIAL_QUERY_INSTABILITY,
span,
QueryInstability { query: cx.tcx.item_name(into_iter_item.def_id) },
);
});
}
}
}
/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
/// `GenericArgs`, and arguments.
fn get_callee_generic_args_and_args<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
) -> Option<(DefId, GenericArgsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
if let ExprKind::Call(callee, args) = expr.kind
&& let callee_ty = cx.typeck_results().expr_ty(callee)
&& let ty::FnDef(callee_def_id, _) = callee_ty.kind()
{
let generic_args = cx.typeck_results().node_args(callee.hir_id);
return Some((*callee_def_id, generic_args, None, args));
}
if let ExprKind::MethodCall(_, recv, args, _) = expr.kind
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
{
let generic_args = cx.typeck_results().node_args(expr.hir_id);
return Some((method_def_id, generic_args, Some(recv), args));
}
None
}
/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
fn get_input_traits_and_projections<'tcx>(
cx: &LateContext<'tcx>,
callee_def_id: DefId,
input: MiddleTy<'tcx>,
) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
let mut trait_predicates = Vec::new();
let mut projection_predicates = Vec::new();
for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
match predicate.kind().skip_binder() {
ClauseKind::Trait(trait_predicate) => {
if trait_predicate.trait_ref.self_ty() == input {
trait_predicates.push(trait_predicate);
}
}
ClauseKind::Projection(projection_predicate) => {
if projection_predicate.projection_term.self_ty() == input {
projection_predicates.push(projection_predicate);
}
}
_ => {}
}
}
(trait_predicates, projection_predicates)
}
declare_tool_lint! {
/// The `usage_of_ty_tykind` lint detects usages of `ty::TyKind::<kind>`,
/// where `ty::<kind>` would suffice.

View File

@ -3934,12 +3934,14 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
// Move up the non-overlapping bindings to the or-pattern.
// Existing bindings just get "merged".
let collected = bindings.pop().unwrap().1;
#[allow(rustc::potential_query_instability)] // FIXME
bindings.last_mut().unwrap().1.extend(collected);
}
// This or-pattern itself can itself be part of a product,
// e.g. `(V1(a) | V2(a), a)` or `(a, V1(a) | V2(a))`.
// Both cases bind `a` again in a product pattern and must be rejected.
let collected = bindings.pop().unwrap().1;
#[allow(rustc::potential_query_instability)] // FIXME
bindings.last_mut().unwrap().1.extend(collected);
// Prevent visiting `ps` as we've already done so above.

View File

@ -47,7 +47,7 @@ pub(crate) struct Cache {
/// Similar to `paths`, but only holds external paths. This is only used for
/// generating explicit hyperlinks to other crates.
pub(crate) external_paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>,
pub(crate) external_paths: FxIndexMap<DefId, (Vec<Symbol>, ItemType)>,
/// Maps local `DefId`s of exported types to fully qualified paths.
/// Unlike 'paths', this mapping ignores any renames that occur

View File

@ -34,4 +34,7 @@ fn main() {
//~^ ERROR using `values_mut` can result in unstable query results
*val = *val + 10;
}
FxHashMap::<u32, i32>::default().extend(x);
//~^ ERROR using `into_iter` can result in unstable query results
}

View File

@ -59,5 +59,13 @@ LL | for val in x.values_mut() {
|
= note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale
error: aborting due to 7 previous errors
error: using `into_iter` can result in unstable query results
--> $DIR/query_stability.rs:38:45
|
LL | FxHashMap::<u32, i32>::default().extend(x);
| ^
|
= note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale
error: aborting due to 8 previous errors