Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
02e812af4d
2562 changed files with 34500 additions and 15442 deletions
|
|
@ -970,7 +970,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
},
|
||||
InlineAsmOperand::SymFn { expr } => {
|
||||
self.hash_expr(expr);
|
||||
}
|
||||
},
|
||||
InlineAsmOperand::Const { anon_const } => {
|
||||
self.hash_body(anon_const.body);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ extern crate rustc_data_structures;
|
|||
extern crate rustc_driver;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_hir;
|
||||
extern crate rustc_hir_analysis;
|
||||
extern crate rustc_hir_typeck;
|
||||
extern crate rustc_index;
|
||||
extern crate rustc_infer;
|
||||
|
|
@ -92,8 +93,9 @@ use std::iter::{once, repeat_n};
|
|||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_abi::Integer;
|
||||
use rustc_ast::ast::{self, LitKind, RangeLimits};
|
||||
use rustc_attr_parsing::{find_attr, AttributeKind};
|
||||
use rustc_attr_parsing::{AttributeKind, find_attr};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_data_structures::unhash::UnhashMap;
|
||||
|
|
@ -107,24 +109,24 @@ use rustc_hir::{
|
|||
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
|
||||
Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind,
|
||||
ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat,
|
||||
PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
|
||||
PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind,
|
||||
TraitItemRef, TraitRef, TyKind, UnOp, def,
|
||||
};
|
||||
use rustc_lexer::{TokenKind, tokenize};
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
use rustc_middle::hir::place::PlaceBase;
|
||||
use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty,
|
||||
TyCtxt, TypeVisitableExt, UintTy, UpvarCapture,
|
||||
TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
|
||||
};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::symbol::{Ident, Symbol, kw};
|
||||
use rustc_span::{InnerSpan, Span, sym};
|
||||
use rustc_abi::Integer;
|
||||
use visitors::{Visitable, for_each_unconsumed_temporary};
|
||||
|
||||
use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
|
||||
|
|
@ -915,22 +917,101 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
|
|||
}
|
||||
|
||||
/// Returns true if the expr is equal to `Default::default` when evaluated.
|
||||
pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
|
||||
pub fn is_default_equivalent_call(
|
||||
cx: &LateContext<'_>,
|
||||
repl_func: &Expr<'_>,
|
||||
whole_call_expr: Option<&Expr<'_>>,
|
||||
) -> bool {
|
||||
if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
|
||||
&& let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
|
||||
&& (is_diag_trait_item(cx, repl_def_id, sym::Default)
|
||||
|| is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the type of the whole method call expression, find the exact method definition, look at
|
||||
// its body and check if it is similar to the corresponding `Default::default()` body.
|
||||
let Some(e) = whole_call_expr else { return false };
|
||||
let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
|
||||
return false;
|
||||
};
|
||||
let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
|
||||
return false;
|
||||
};
|
||||
let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
|
||||
if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
|
||||
cx.tcx.lifetimes.re_erased.into()
|
||||
} else if param.index == 0 && param.name == kw::SelfUpper {
|
||||
ty.into()
|
||||
} else {
|
||||
param.to_error(cx.tcx)
|
||||
}
|
||||
});
|
||||
let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
|
||||
|
||||
let Ok(Some(instance)) = instance else { return false };
|
||||
if let rustc_ty::InstanceKind::Item(def) = instance.def
|
||||
&& !cx.tcx.is_mir_available(def)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
|
||||
return false;
|
||||
};
|
||||
let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Get the MIR Body for the `<Ty as Default>::default()` function.
|
||||
// If it is a value or call (either fn or ctor), we compare its `DefId` against the one for the
|
||||
// resolution of the expression we had in the path. This lets us identify, for example, that
|
||||
// the body of `<Vec<T> as Default>::default()` is a `Vec::new()`, and the field was being
|
||||
// initialized to `Vec::new()` as well.
|
||||
let body = cx.tcx.instance_mir(instance.def);
|
||||
for block_data in body.basic_blocks.iter() {
|
||||
if block_data.statements.len() == 1
|
||||
&& let StatementKind::Assign(assign) = &block_data.statements[0].kind
|
||||
&& assign.0.local == RETURN_PLACE
|
||||
&& let Rvalue::Aggregate(kind, _places) = &assign.1
|
||||
&& let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
|
||||
&& let def = cx.tcx.adt_def(did)
|
||||
&& let variant = &def.variant(*variant_index)
|
||||
&& variant.fields.is_empty()
|
||||
&& let Some((_, did)) = variant.ctor
|
||||
&& did == repl_def_id
|
||||
{
|
||||
return true;
|
||||
} else if block_data.statements.is_empty()
|
||||
&& let Some(term) = &block_data.terminator
|
||||
{
|
||||
match &term.kind {
|
||||
TerminatorKind::Call {
|
||||
func: Operand::Constant(c),
|
||||
..
|
||||
} if let rustc_ty::FnDef(did, _args) = c.ty().kind()
|
||||
&& *did == repl_def_id =>
|
||||
{
|
||||
return true;
|
||||
},
|
||||
TerminatorKind::TailCall {
|
||||
func: Operand::Constant(c),
|
||||
..
|
||||
} if let rustc_ty::FnDef(did, _args) = c.ty().kind()
|
||||
&& *did == repl_def_id =>
|
||||
{
|
||||
return true;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
|
||||
/// Returns true if the expr is equal to `Default::default()` of its type when evaluated.
|
||||
///
|
||||
/// It doesn't cover all cases, for example indirect function calls (some of std
|
||||
/// functions are supported) but it is the best we have.
|
||||
/// It doesn't cover all cases, like struct literals, but it is a close approximation.
|
||||
pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
match &e.kind {
|
||||
ExprKind::Lit(lit) => match lit.node {
|
||||
|
|
@ -951,7 +1032,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
|||
false
|
||||
}
|
||||
},
|
||||
ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func),
|
||||
ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
|
||||
ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
|
||||
ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
|
||||
ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
|
||||
|
|
@ -1421,6 +1502,10 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
|
|||
| Node::ImplItem(&ImplItem {
|
||||
kind: ImplItemKind::Fn(_, eid),
|
||||
..
|
||||
})
|
||||
| Node::TraitItem(&TraitItem {
|
||||
kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
|
||||
..
|
||||
}) => match cx.tcx.hir_body(eid).value.kind {
|
||||
ExprKind::Block(block, _) => Some(block),
|
||||
_ => None,
|
||||
|
|
@ -1971,8 +2056,7 @@ pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool
|
|||
/// Checks if the given HIR node is inside an `impl` block with the `automatically_derived`
|
||||
/// attribute.
|
||||
pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
||||
tcx
|
||||
.hir_parent_owner_iter(id)
|
||||
tcx.hir_parent_owner_iter(id)
|
||||
.filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
|
||||
.any(|(id, _)| {
|
||||
has_attr(
|
||||
|
|
@ -2618,9 +2702,7 @@ pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
|||
|
||||
/// Checks if any parent node of `HirId` has `#[cfg(test)]` attribute applied
|
||||
pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
||||
tcx
|
||||
.hir_parent_id_iter(id)
|
||||
.any(|parent_id| is_cfg_test(tcx, parent_id))
|
||||
tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
|
||||
}
|
||||
|
||||
/// Checks if the node is in a `#[test]` function or has any parent node marked `#[cfg(test)]`
|
||||
|
|
@ -3504,3 +3586,111 @@ pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'t
|
|||
})
|
||||
.is_break()
|
||||
}
|
||||
|
||||
/// Returns true if the specified `expr` requires coercion,
|
||||
/// meaning that it either has a coercion or propagates a coercion from one of its sub expressions.
|
||||
///
|
||||
/// Similar to [`is_adjusted`], this not only checks if an expression's type was adjusted,
|
||||
/// but also going through extra steps to see if it fits the description of [coercion sites].
|
||||
///
|
||||
/// You should used this when you want to avoid suggesting replacing an expression that is currently
|
||||
/// a coercion site or coercion propagating expression with one that is not.
|
||||
///
|
||||
/// [coercion sites]: https://doc.rust-lang.org/stable/reference/type-coercions.html#coercion-sites
|
||||
pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
|
||||
let expr_ty_is_adjusted = cx
|
||||
.typeck_results()
|
||||
.expr_adjustments(expr)
|
||||
.iter()
|
||||
// ignore `NeverToAny` adjustments, such as `panic!` call.
|
||||
.any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
|
||||
if expr_ty_is_adjusted {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Identify coercion sites and recursively check if those sites
|
||||
// actually have type adjustments.
|
||||
match expr.kind {
|
||||
ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
|
||||
let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
|
||||
|
||||
if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
|
||||
let mut args_with_ty_param = {
|
||||
fn_sig
|
||||
.inputs()
|
||||
.skip_binder()
|
||||
.iter()
|
||||
.skip(self_arg_count)
|
||||
.zip(args)
|
||||
.filter_map(|(arg_ty, arg)| {
|
||||
if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
|
||||
Some(arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
|
||||
},
|
||||
// Struct/union initialization.
|
||||
ExprKind::Struct(qpath, _, _) => {
|
||||
let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
|
||||
if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
|
||||
let generic_args = cx.typeck_results().node_args(expr.hir_id);
|
||||
v_def
|
||||
.fields
|
||||
.iter()
|
||||
.any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
// Function results, including the final line of a block or a `return` expression.
|
||||
ExprKind::Block(
|
||||
&Block {
|
||||
expr: Some(ret_expr), ..
|
||||
},
|
||||
_,
|
||||
)
|
||||
| ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
|
||||
|
||||
// ===== Coercion-propagation expressions =====
|
||||
ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
|
||||
// Array but with repeating syntax.
|
||||
ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
|
||||
// Others that may contain coercion sites.
|
||||
ExprKind::If(_, then, maybe_else) => {
|
||||
expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
|
||||
},
|
||||
ExprKind::Match(_, arms, _) => arms
|
||||
.iter()
|
||||
.map(|arm| arm.body)
|
||||
.any(|body| expr_requires_coercion(cx, body)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `expr` designates a mutable static, a mutable local binding, or an expression
|
||||
/// that can be owned.
|
||||
pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let Some(hir_id) = path_to_local(expr)
|
||||
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
|
||||
{
|
||||
matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
|
||||
} else if let ExprKind::Path(p) = &expr.kind
|
||||
&& let Some(mutability) = cx
|
||||
.qpath_res(p, expr.hir_id)
|
||||
.opt_def_id()
|
||||
.and_then(|id| cx.tcx.static_mutability(id))
|
||||
{
|
||||
mutability == Mutability::Mut
|
||||
} else if let ExprKind::Field(parent, _) = expr.kind {
|
||||
is_mutable(cx, parent)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
|
|||
sym::print_macro,
|
||||
sym::println_macro,
|
||||
sym::std_panic_macro,
|
||||
sym::todo_macro,
|
||||
sym::unimplemented_macro,
|
||||
sym::write_macro,
|
||||
sym::writeln_macro,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ msrv_aliases! {
|
|||
1,77,0 { C_STR_LITERALS }
|
||||
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
|
||||
1,75,0 { OPTION_AS_SLICE }
|
||||
1,74,0 { REPR_RUST }
|
||||
1,74,0 { REPR_RUST, IO_ERROR_OTHER }
|
||||
1,73,0 { MANUAL_DIV_CEIL }
|
||||
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
|
||||
1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN }
|
||||
|
|
@ -37,10 +37,11 @@ msrv_aliases! {
|
|||
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN }
|
||||
1,59,0 { THREAD_LOCAL_CONST_INIT }
|
||||
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF }
|
||||
1,57,0 { MAP_WHILE }
|
||||
1,56,0 { CONST_FN_UNION }
|
||||
1,55,0 { SEEK_REWIND }
|
||||
1,54,0 { INTO_KEYS }
|
||||
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
|
||||
1,53,0 { OR_PATTERNS, INTEGER_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
|
||||
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
|
||||
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
|
||||
1,50,0 { BOOL_THEN, CLAMP, SLICE_FILL }
|
||||
|
|
@ -57,6 +58,7 @@ msrv_aliases! {
|
|||
1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
|
||||
1,34,0 { TRY_FROM }
|
||||
1,33,0 { UNDERSCORE_IMPORTS }
|
||||
1,31,0 { OPTION_REPLACE }
|
||||
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
|
||||
1,29,0 { ITER_FLATTEN }
|
||||
1,28,0 { FROM_BOOL, REPEAT_WITH }
|
||||
|
|
|
|||
|
|
@ -28,13 +28,9 @@ pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
|
|||
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
|
||||
|
||||
// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items.
|
||||
pub const ABORT: [&str; 3] = ["std", "process", "abort"];
|
||||
pub const CHILD: [&str; 3] = ["std", "process", "Child"];
|
||||
pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"];
|
||||
pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"];
|
||||
pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
|
||||
pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"];
|
||||
pub const STDIN: [&str; 4] = ["std", "io", "stdio", "Stdin"];
|
||||
pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"];
|
||||
pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"];
|
||||
|
||||
// Paths in clippy itself
|
||||
pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ impl SourceFileRange {
|
|||
}
|
||||
}
|
||||
|
||||
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
|
||||
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block` with no label.
|
||||
pub fn expr_block(
|
||||
sess: &impl HasSession,
|
||||
expr: &Expr<'_>,
|
||||
|
|
@ -305,10 +305,10 @@ pub fn expr_block(
|
|||
) -> String {
|
||||
let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app);
|
||||
if !from_macro
|
||||
&& let ExprKind::Block(block, _) = expr.kind
|
||||
&& let ExprKind::Block(block, None) = expr.kind
|
||||
&& block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||
{
|
||||
format!("{code}")
|
||||
code
|
||||
} else {
|
||||
// FIXME: add extra indent for the unsafe blocks:
|
||||
// original code: unsafe { ... }
|
||||
|
|
@ -421,11 +421,10 @@ pub fn position_before_rarrow(s: &str) -> Option<usize> {
|
|||
}
|
||||
|
||||
/// Reindent a multiline string with possibility of ignoring the first line.
|
||||
#[expect(clippy::needless_pass_by_value)]
|
||||
pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
|
||||
let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
|
||||
pub fn reindent_multiline(s: &str, ignore_first: bool, indent: Option<usize>) -> String {
|
||||
let s_space = reindent_multiline_inner(s, ignore_first, indent, ' ');
|
||||
let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
|
||||
reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
|
||||
reindent_multiline_inner(&s_tab, ignore_first, indent, ' ')
|
||||
}
|
||||
|
||||
fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
|
||||
|
|
@ -553,42 +552,37 @@ pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option<String> {
|
|||
/// } // aligned with `if`
|
||||
/// ```
|
||||
/// Note that the first line of the snippet always has 0 indentation.
|
||||
pub fn snippet_block<'a>(
|
||||
sess: &impl HasSession,
|
||||
span: Span,
|
||||
default: &'a str,
|
||||
indent_relative_to: Option<Span>,
|
||||
) -> Cow<'a, str> {
|
||||
pub fn snippet_block(sess: &impl HasSession, span: Span, default: &str, indent_relative_to: Option<Span>) -> String {
|
||||
let snip = snippet(sess, span, default);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
|
||||
reindent_multiline(snip, true, indent)
|
||||
reindent_multiline(&snip, true, indent)
|
||||
}
|
||||
|
||||
/// Same as `snippet_block`, but adapts the applicability level by the rules of
|
||||
/// `snippet_with_applicability`.
|
||||
pub fn snippet_block_with_applicability<'a>(
|
||||
pub fn snippet_block_with_applicability(
|
||||
sess: &impl HasSession,
|
||||
span: Span,
|
||||
default: &'a str,
|
||||
default: &str,
|
||||
indent_relative_to: Option<Span>,
|
||||
applicability: &mut Applicability,
|
||||
) -> Cow<'a, str> {
|
||||
) -> String {
|
||||
let snip = snippet_with_applicability(sess, span, default, applicability);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
|
||||
reindent_multiline(snip, true, indent)
|
||||
reindent_multiline(&snip, true, indent)
|
||||
}
|
||||
|
||||
pub fn snippet_block_with_context<'a>(
|
||||
pub fn snippet_block_with_context(
|
||||
sess: &impl HasSession,
|
||||
span: Span,
|
||||
outer: SyntaxContext,
|
||||
default: &'a str,
|
||||
default: &str,
|
||||
indent_relative_to: Option<Span>,
|
||||
app: &mut Applicability,
|
||||
) -> (Cow<'a, str>, bool) {
|
||||
) -> (String, bool) {
|
||||
let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
|
||||
(reindent_multiline(snip, true, indent), from_macro)
|
||||
(reindent_multiline(&snip, true, indent), from_macro)
|
||||
}
|
||||
|
||||
/// Same as `snippet_with_applicability`, but first walks the span up to the given context.
|
||||
|
|
@ -749,11 +743,11 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_reindent_multiline_single_line() {
|
||||
assert_eq!("", reindent_multiline("".into(), false, None));
|
||||
assert_eq!("...", reindent_multiline("...".into(), false, None));
|
||||
assert_eq!("...", reindent_multiline(" ...".into(), false, None));
|
||||
assert_eq!("...", reindent_multiline("\t...".into(), false, None));
|
||||
assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
|
||||
assert_eq!("", reindent_multiline("", false, None));
|
||||
assert_eq!("...", reindent_multiline("...", false, None));
|
||||
assert_eq!("...", reindent_multiline(" ...", false, None));
|
||||
assert_eq!("...", reindent_multiline("\t...", false, None));
|
||||
assert_eq!("...", reindent_multiline("\t\t...", false, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -768,7 +762,7 @@ mod test {
|
|||
y
|
||||
} else {
|
||||
z
|
||||
}".into(), false, None));
|
||||
}", false, None));
|
||||
assert_eq!("\
|
||||
if x {
|
||||
\ty
|
||||
|
|
@ -778,7 +772,7 @@ mod test {
|
|||
\ty
|
||||
} else {
|
||||
\tz
|
||||
}".into(), false, None));
|
||||
}", false, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -795,7 +789,7 @@ mod test {
|
|||
|
||||
} else {
|
||||
z
|
||||
}".into(), false, None));
|
||||
}", false, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -811,6 +805,6 @@ mod test {
|
|||
y
|
||||
} else {
|
||||
z
|
||||
}".into(), true, Some(8)));
|
||||
}", true, Some(8)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Expr, FnDecl, LangItem, TyKind};
|
||||
use rustc_hir_analysis::lower_ty;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::ConstValue;
|
||||
|
|
@ -36,6 +37,19 @@ use crate::{def_path_def_ids, match_def_path, path_res};
|
|||
mod type_certainty;
|
||||
pub use type_certainty::expr_type_is_certain;
|
||||
|
||||
/// Lower a [`hir::Ty`] to a [`rustc_middle::Ty`].
|
||||
pub fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
|
||||
cx.maybe_typeck_results()
|
||||
.and_then(|results| {
|
||||
if results.hir_owner == hir_ty.hir_id.owner {
|
||||
results.node_type_opt(hir_ty.hir_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| lower_ty(cx.tcx, hir_ty))
|
||||
}
|
||||
|
||||
/// Checks if the given type implements copy.
|
||||
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
cx.type_is_copy_modulo_regions(ty)
|
||||
|
|
@ -351,20 +365,26 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
|||
/// Checks if `Ty` is normalizable. This function is useful
|
||||
/// to avoid crashes on `layout_of`.
|
||||
pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
|
||||
is_normalizable_helper(cx, param_env, ty, 0, &mut FxHashMap::default())
|
||||
}
|
||||
|
||||
fn is_normalizable_helper<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
depth: usize,
|
||||
cache: &mut FxHashMap<Ty<'tcx>, bool>,
|
||||
) -> bool {
|
||||
if let Some(&cached_result) = cache.get(&ty) {
|
||||
return cached_result;
|
||||
}
|
||||
// prevent recursive loops, false-negative is better than endless loop leading to stack overflow
|
||||
cache.insert(ty, false);
|
||||
if !cx.tcx.recursion_limit().value_within_limit(depth) {
|
||||
return false;
|
||||
}
|
||||
// Prevent recursive loops by answering `true` to recursive requests with the same
|
||||
// type. This will be adjusted when the outermost call analyzes all the type
|
||||
// components.
|
||||
cache.insert(ty, true);
|
||||
let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode());
|
||||
let cause = ObligationCause::dummy();
|
||||
let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() {
|
||||
|
|
@ -373,11 +393,11 @@ fn is_normalizable_helper<'tcx>(
|
|||
variant
|
||||
.fields
|
||||
.iter()
|
||||
.all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), cache))
|
||||
.all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), depth + 1, cache))
|
||||
}),
|
||||
_ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
|
||||
GenericArgKind::Type(inner_ty) if inner_ty != ty => {
|
||||
is_normalizable_helper(cx, param_env, inner_ty, cache)
|
||||
is_normalizable_helper(cx, param_env, inner_ty, depth + 1, cache)
|
||||
},
|
||||
_ => true, // if inner_ty == ty, we've already checked it
|
||||
}),
|
||||
|
|
@ -1227,6 +1247,10 @@ impl<'tcx> InteriorMut<'tcx> {
|
|||
.find_map(|f| self.interior_mut_ty_chain(cx, f.ty(cx.tcx, args)))
|
||||
}
|
||||
},
|
||||
ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) {
|
||||
Ok(normalized_ty) if ty != normalized_ty => self.interior_mut_ty_chain(cx, normalized_ty),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue