mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-15 05:26:47 +00:00
Merge 420b9864b7
into 65fa0ab924
This commit is contained in:
commit
3ff09b8635
@ -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?
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user