Merge from rustc
This commit is contained in:
commit
f25d8a6189
429 changed files with 7549 additions and 3574 deletions
14
Cargo.lock
14
Cargo.lock
|
|
@ -4168,6 +4168,7 @@ dependencies = [
|
|||
name = "rustc_parse_format"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_data_structures",
|
||||
"rustc_lexer",
|
||||
]
|
||||
|
||||
|
|
@ -4342,6 +4343,7 @@ dependencies = [
|
|||
"rustc_feature",
|
||||
"rustc_fs_util",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_lint_defs",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
|
|
@ -4402,6 +4404,7 @@ dependencies = [
|
|||
"rustc_span",
|
||||
"rustc_target",
|
||||
"tracing",
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5392,6 +5395,17 @@ dependencies = [
|
|||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "1.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"rand 0.8.5",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "type-map"
|
||||
version = "0.4.0"
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
|
|||
self.check_activations(location);
|
||||
|
||||
match &terminator.kind {
|
||||
TerminatorKind::SwitchInt { discr, switch_ty: _, targets: _ } => {
|
||||
TerminatorKind::SwitchInt { discr, targets: _ } => {
|
||||
self.consume_operand(location, discr);
|
||||
}
|
||||
TerminatorKind::Drop { place: drop_place, target: _, unwind: _ } => {
|
||||
|
|
|
|||
|
|
@ -644,7 +644,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
|
|||
self.check_activations(loc, span, flow_state);
|
||||
|
||||
match &term.kind {
|
||||
TerminatorKind::SwitchInt { discr, switch_ty: _, targets: _ } => {
|
||||
TerminatorKind::SwitchInt { discr, targets: _ } => {
|
||||
self.consume_operand(loc, (discr, span), flow_state);
|
||||
}
|
||||
TerminatorKind::Drop { place, target: _, unwind: _ } => {
|
||||
|
|
|
|||
|
|
@ -1360,25 +1360,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
}
|
||||
TerminatorKind::SwitchInt { discr, switch_ty, .. } => {
|
||||
TerminatorKind::SwitchInt { discr, .. } => {
|
||||
self.check_operand(discr, term_location);
|
||||
|
||||
let discr_ty = discr.ty(body, tcx);
|
||||
if let Err(terr) = self.sub_types(
|
||||
discr_ty,
|
||||
*switch_ty,
|
||||
term_location.to_locations(),
|
||||
ConstraintCategory::Assignment,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
term,
|
||||
"bad SwitchInt ({:?} on {:?}): {:?}",
|
||||
switch_ty,
|
||||
discr_ty,
|
||||
terr
|
||||
);
|
||||
}
|
||||
let switch_ty = discr.ty(body, tcx);
|
||||
if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() {
|
||||
span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pub fn expand_concat(
|
|||
sp: rustc_span::Span,
|
||||
tts: TokenStream,
|
||||
) -> Box<dyn base::MacResult + 'static> {
|
||||
let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
|
||||
let Some(es) = base::get_exprs_from_tts(cx, tts) else {
|
||||
return DummyResult::any(sp);
|
||||
};
|
||||
let mut accumulator = String::new();
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ pub fn expand_concat_bytes(
|
|||
sp: rustc_span::Span,
|
||||
tts: TokenStream,
|
||||
) -> Box<dyn base::MacResult + 'static> {
|
||||
let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
|
||||
let Some(es) = base::get_exprs_from_tts(cx, tts) else {
|
||||
return DummyResult::any(sp);
|
||||
};
|
||||
let mut accumulator = Vec::new();
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ pub fn expand_env<'cx>(
|
|||
sp: Span,
|
||||
tts: TokenStream,
|
||||
) -> Box<dyn base::MacResult + 'cx> {
|
||||
let mut exprs = match get_exprs_from_tts(cx, sp, tts) {
|
||||
let mut exprs = match get_exprs_from_tts(cx, tts) {
|
||||
Some(exprs) if exprs.is_empty() => {
|
||||
cx.span_err(sp, "env! takes 1 or 2 arguments");
|
||||
return DummyResult::any(sp);
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ pub fn make_format_args(
|
|||
parse::Piece::String(s) => {
|
||||
unfinished_literal.push_str(s);
|
||||
}
|
||||
parse::Piece::NextArgument(parse::Argument { position, position_span, format }) => {
|
||||
parse::Piece::NextArgument(box parse::Argument { position, position_span, format }) => {
|
||||
if !unfinished_literal.is_empty() {
|
||||
template.push(FormatArgsPiece::Literal(Symbol::intern(&unfinished_literal)));
|
||||
unfinished_literal.clear();
|
||||
|
|
|
|||
|
|
@ -372,8 +372,10 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
}
|
||||
}
|
||||
|
||||
TerminatorKind::SwitchInt { discr, switch_ty, targets } => {
|
||||
let discr = codegen_operand(fx, discr).load_scalar(fx);
|
||||
TerminatorKind::SwitchInt { discr, targets } => {
|
||||
let discr = codegen_operand(fx, discr);
|
||||
let switch_ty = discr.layout().ty;
|
||||
let discr = discr.load_scalar(fx);
|
||||
|
||||
let use_bool_opt = switch_ty.kind() == fx.tcx.types.bool.kind()
|
||||
|| (targets.iter().count() == 1 && targets.iter().next().unwrap().0 == 0);
|
||||
|
|
|
|||
|
|
@ -300,4 +300,8 @@ impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
|||
// Unsupported.
|
||||
self.context.new_rvalue_from_int(self.int_type, 0)
|
||||
}
|
||||
|
||||
fn set_kcfi_type_metadata(&self, _function: RValue<'gcc>, _kcfi_typeid: u32) {
|
||||
// Unsupported.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,8 @@ pub(crate) unsafe fn codegen(
|
|||
callee,
|
||||
args.as_ptr(),
|
||||
args.len() as c_uint,
|
||||
None,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
llvm::LLVMSetTailCall(ret, True);
|
||||
if output.is_some() {
|
||||
|
|
@ -132,8 +133,15 @@ pub(crate) unsafe fn codegen(
|
|||
.enumerate()
|
||||
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
|
||||
.collect::<Vec<_>>();
|
||||
let ret =
|
||||
llvm::LLVMRustBuildCall(llbuilder, ty, callee, args.as_ptr(), args.len() as c_uint, None);
|
||||
let ret = llvm::LLVMRustBuildCall(
|
||||
llbuilder,
|
||||
ty,
|
||||
callee,
|
||||
args.as_ptr(),
|
||||
args.len() as c_uint,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
llvm::LLVMSetTailCall(ret, True);
|
||||
llvm::LLVMBuildRetVoid(llbuilder);
|
||||
llvm::LLVMDisposeBuilder(llbuilder);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use rustc_middle::ty::layout::{
|
|||
};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use rustc_symbol_mangling::typeid::kcfi_typeid_for_fnabi;
|
||||
use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
|
||||
use rustc_target::spec::{HasTargetSpec, Target};
|
||||
use std::borrow::Cow;
|
||||
|
|
@ -225,9 +226,25 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
debug!("invoke {:?} with args ({:?})", llfn, args);
|
||||
|
||||
let args = self.check_call("invoke", llty, llfn, args);
|
||||
let bundle = funclet.map(|funclet| funclet.bundle());
|
||||
let bundle = bundle.as_ref().map(|b| &*b.raw);
|
||||
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
|
||||
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
|
||||
let mut bundles = vec![funclet_bundle];
|
||||
|
||||
// Set KCFI operand bundle
|
||||
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
|
||||
let kcfi_bundle =
|
||||
if self.tcx.sess.is_sanitizer_kcfi_enabled() && fn_abi.is_some() && is_indirect_call {
|
||||
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap());
|
||||
Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if kcfi_bundle.is_some() {
|
||||
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
|
||||
bundles.push(kcfi_bundle);
|
||||
}
|
||||
|
||||
bundles.retain(|bundle| bundle.is_some());
|
||||
let invoke = unsafe {
|
||||
llvm::LLVMRustBuildInvoke(
|
||||
self.llbuilder,
|
||||
|
|
@ -237,7 +254,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
args.len() as c_uint,
|
||||
then,
|
||||
catch,
|
||||
bundle,
|
||||
bundles.as_ptr(),
|
||||
bundles.len() as c_uint,
|
||||
UNNAMED,
|
||||
)
|
||||
};
|
||||
|
|
@ -1143,7 +1161,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
None,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1159,9 +1178,25 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
debug!("call {:?} with args ({:?})", llfn, args);
|
||||
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
let bundle = funclet.map(|funclet| funclet.bundle());
|
||||
let bundle = bundle.as_ref().map(|b| &*b.raw);
|
||||
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
|
||||
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
|
||||
let mut bundles = vec![funclet_bundle];
|
||||
|
||||
// Set KCFI operand bundle
|
||||
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
|
||||
let kcfi_bundle =
|
||||
if self.tcx.sess.is_sanitizer_kcfi_enabled() && fn_abi.is_some() && is_indirect_call {
|
||||
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap());
|
||||
Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if kcfi_bundle.is_some() {
|
||||
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
|
||||
bundles.push(kcfi_bundle);
|
||||
}
|
||||
|
||||
bundles.retain(|bundle| bundle.is_some());
|
||||
let call = unsafe {
|
||||
llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
|
|
@ -1169,7 +1204,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
bundle,
|
||||
bundles.as_ptr(),
|
||||
bundles.len() as c_uint,
|
||||
)
|
||||
};
|
||||
if let Some(fn_abi) = fn_abi {
|
||||
|
|
|
|||
|
|
@ -250,6 +250,11 @@ pub unsafe fn create_module<'ll>(
|
|||
);
|
||||
}
|
||||
|
||||
if sess.is_sanitizer_kcfi_enabled() {
|
||||
let kcfi = "kcfi\0".as_ptr().cast();
|
||||
llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
|
||||
}
|
||||
|
||||
// Control Flow Guard is currently only supported by the MSVC linker on Windows.
|
||||
if sess.target.is_like_msvc {
|
||||
match sess.opts.cg.control_flow_guard {
|
||||
|
|
|
|||
|
|
@ -27,9 +27,7 @@ use rustc_codegen_ssa::traits::*;
|
|||
use rustc_fs_util::path_to_c_string;
|
||||
use rustc_hir::def::CtorKind;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_index::vec::{Idx, IndexVec};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::{self, GeneratorLayout};
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_middle::ty::{
|
||||
|
|
@ -1026,33 +1024,6 @@ fn build_struct_type_di_node<'ll, 'tcx>(
|
|||
// Tuples
|
||||
//=-----------------------------------------------------------------------------
|
||||
|
||||
/// Returns names of captured upvars for closures and generators.
|
||||
///
|
||||
/// Here are some examples:
|
||||
/// - `name__field1__field2` when the upvar is captured by value.
|
||||
/// - `_ref__name__field` when the upvar is captured by reference.
|
||||
///
|
||||
/// For generators this only contains upvars that are shared by all states.
|
||||
fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> SmallVec<String> {
|
||||
let body = tcx.optimized_mir(def_id);
|
||||
|
||||
body.var_debug_info
|
||||
.iter()
|
||||
.filter_map(|var| {
|
||||
let is_ref = match var.value {
|
||||
mir::VarDebugInfoContents::Place(place) if place.local == mir::Local::new(1) => {
|
||||
// The projection is either `[.., Field, Deref]` or `[.., Field]`. It
|
||||
// implies whether the variable is captured by value or by reference.
|
||||
matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let prefix = if is_ref { "_ref__" } else { "" };
|
||||
Some(prefix.to_owned() + var.name.as_str())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Builds the DW_TAG_member debuginfo nodes for the upvars of a closure or generator.
|
||||
/// For a generator, this will handle upvars shared by all states.
|
||||
fn build_upvar_field_di_nodes<'ll, 'tcx>(
|
||||
|
|
@ -1083,7 +1054,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>(
|
|||
.all(|&t| t == cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t))
|
||||
);
|
||||
|
||||
let capture_names = closure_saved_names_of_captured_variables(cx.tcx, def_id);
|
||||
let capture_names = cx.tcx.closure_saved_names_of_captured_variables(def_id);
|
||||
let layout = cx.layout_of(closure_or_generator_ty);
|
||||
|
||||
up_var_tys
|
||||
|
|
@ -1229,43 +1200,6 @@ fn build_union_type_di_node<'ll, 'tcx>(
|
|||
)
|
||||
}
|
||||
|
||||
// FIXME(eddyb) maybe precompute this? Right now it's computed once
|
||||
// per generator monomorphization, but it doesn't depend on substs.
|
||||
fn generator_layout_and_saved_local_names<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
) -> (&'tcx GeneratorLayout<'tcx>, IndexVec<mir::GeneratorSavedLocal, Option<Symbol>>) {
|
||||
let body = tcx.optimized_mir(def_id);
|
||||
let generator_layout = body.generator_layout().unwrap();
|
||||
let mut generator_saved_local_names = IndexVec::from_elem(None, &generator_layout.field_tys);
|
||||
|
||||
let state_arg = mir::Local::new(1);
|
||||
for var in &body.var_debug_info {
|
||||
let mir::VarDebugInfoContents::Place(place) = &var.value else { continue };
|
||||
if place.local != state_arg {
|
||||
continue;
|
||||
}
|
||||
match place.projection[..] {
|
||||
[
|
||||
// Deref of the `Pin<&mut Self>` state argument.
|
||||
mir::ProjectionElem::Field(..),
|
||||
mir::ProjectionElem::Deref,
|
||||
// Field of a variant of the state.
|
||||
mir::ProjectionElem::Downcast(_, variant),
|
||||
mir::ProjectionElem::Field(field, _),
|
||||
] => {
|
||||
let name = &mut generator_saved_local_names
|
||||
[generator_layout.variant_fields[variant][field]];
|
||||
if name.is_none() {
|
||||
name.replace(var.name);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
(generator_layout, generator_saved_local_names)
|
||||
}
|
||||
|
||||
/// Computes the type parameters for a type, if any, for the given metadata.
|
||||
fn build_generic_type_param_di_nodes<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ use crate::{
|
|||
common::CodegenCx,
|
||||
debuginfo::{
|
||||
metadata::{
|
||||
build_field_di_node, closure_saved_names_of_captured_variables,
|
||||
build_field_di_node,
|
||||
enums::{tag_base_type, DiscrResult},
|
||||
file_metadata, generator_layout_and_saved_local_names, size_and_align_of, type_di_node,
|
||||
file_metadata, size_and_align_of, type_di_node,
|
||||
type_map::{self, Stub, UniqueTypeId},
|
||||
unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, NO_SCOPE_METADATA,
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
|
|
@ -677,9 +677,9 @@ fn build_union_fields_for_direct_tag_generator<'ll, 'tcx>(
|
|||
};
|
||||
|
||||
let (generator_layout, state_specific_upvar_names) =
|
||||
generator_layout_and_saved_local_names(cx.tcx, generator_def_id);
|
||||
cx.tcx.generator_layout_and_saved_local_names(generator_def_id);
|
||||
|
||||
let common_upvar_names = closure_saved_names_of_captured_variables(cx.tcx, generator_def_id);
|
||||
let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(generator_def_id);
|
||||
let variant_range = generator_substs.variant_range(generator_def_id, cx.tcx);
|
||||
let variant_count = (variant_range.start.as_u32()..variant_range.end.as_u32()).len();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ use crate::{
|
|||
common::CodegenCx,
|
||||
debuginfo::{
|
||||
metadata::{
|
||||
closure_saved_names_of_captured_variables,
|
||||
enums::tag_base_type,
|
||||
file_metadata, generator_layout_and_saved_local_names, size_and_align_of, type_di_node,
|
||||
file_metadata, size_and_align_of, type_di_node,
|
||||
type_map::{self, Stub, StubInfo, UniqueTypeId},
|
||||
unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS,
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
|
|
@ -157,7 +156,7 @@ pub(super) fn build_generator_di_node<'ll, 'tcx>(
|
|||
),
|
||||
|cx, generator_type_di_node| {
|
||||
let (generator_layout, state_specific_upvar_names) =
|
||||
generator_layout_and_saved_local_names(cx.tcx, generator_def_id);
|
||||
cx.tcx.generator_layout_and_saved_local_names(generator_def_id);
|
||||
|
||||
let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = generator_type_and_layout.variants else {
|
||||
bug!(
|
||||
|
|
@ -167,7 +166,7 @@ pub(super) fn build_generator_di_node<'ll, 'tcx>(
|
|||
};
|
||||
|
||||
let common_upvar_names =
|
||||
closure_saved_names_of_captured_variables(cx.tcx, generator_def_id);
|
||||
cx.tcx.closure_saved_names_of_captured_variables(generator_def_id);
|
||||
|
||||
// Build variant struct types
|
||||
let variant_struct_type_di_nodes: SmallVec<_> = variants
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use crate::type_::Type;
|
|||
use crate::value::Value;
|
||||
use rustc_codegen_ssa::traits::TypeMembershipMethods;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_symbol_mangling::typeid::typeid_for_fnabi;
|
||||
use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// Declare a function.
|
||||
|
|
@ -136,6 +136,11 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
|||
self.set_type_metadata(llfn, typeid);
|
||||
}
|
||||
|
||||
if self.tcx.sess.is_sanitizer_kcfi_enabled() {
|
||||
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
|
||||
self.set_kcfi_type_metadata(llfn, kcfi_typeid);
|
||||
}
|
||||
|
||||
llfn
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -427,6 +427,7 @@ pub enum MetadataType {
|
|||
MD_type = 19,
|
||||
MD_vcall_visibility = 28,
|
||||
MD_noundef = 29,
|
||||
MD_kcfi_type = 36,
|
||||
}
|
||||
|
||||
/// LLVMRustAsmDialect
|
||||
|
|
@ -1063,6 +1064,7 @@ extern "C" {
|
|||
pub fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
|
||||
pub fn LLVMRustGlobalAddMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
|
||||
pub fn LLVMValueAsMetadata(Node: &Value) -> &Metadata;
|
||||
pub fn LLVMIsAFunction(Val: &Value) -> Option<&Value>;
|
||||
|
||||
// Operations on constants of any type
|
||||
pub fn LLVMConstNull(Ty: &Type) -> &Value;
|
||||
|
|
@ -1273,7 +1275,8 @@ extern "C" {
|
|||
NumArgs: c_uint,
|
||||
Then: &'a BasicBlock,
|
||||
Catch: &'a BasicBlock,
|
||||
Bundle: Option<&OperandBundleDef<'a>>,
|
||||
OpBundles: *const Option<&OperandBundleDef<'a>>,
|
||||
NumOpBundles: c_uint,
|
||||
Name: *const c_char,
|
||||
) -> &'a Value;
|
||||
pub fn LLVMBuildLandingPad<'a>(
|
||||
|
|
@ -1643,7 +1646,8 @@ extern "C" {
|
|||
Fn: &'a Value,
|
||||
Args: *const &'a Value,
|
||||
NumArgs: c_uint,
|
||||
Bundle: Option<&OperandBundleDef<'a>>,
|
||||
OpBundles: *const Option<&OperandBundleDef<'a>>,
|
||||
NumOpBundles: c_uint,
|
||||
) -> &'a Value;
|
||||
pub fn LLVMRustBuildMemCpy<'a>(
|
||||
B: &Builder<'a>,
|
||||
|
|
|
|||
|
|
@ -316,4 +316,19 @@ impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
|
||||
let kcfi_type_metadata = self.const_u32(kcfi_typeid);
|
||||
unsafe {
|
||||
llvm::LLVMGlobalSetMetadata(
|
||||
function,
|
||||
llvm::MD_kcfi_type as c_uint,
|
||||
llvm::LLVMMDNodeInContext2(
|
||||
self.llcx,
|
||||
&llvm::LLVMValueAsMetadata(kcfi_type_metadata),
|
||||
1,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -307,12 +307,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
helper: TerminatorCodegenHelper<'tcx>,
|
||||
bx: &mut Bx,
|
||||
discr: &mir::Operand<'tcx>,
|
||||
switch_ty: Ty<'tcx>,
|
||||
targets: &SwitchTargets,
|
||||
) {
|
||||
let discr = self.codegen_operand(bx, &discr);
|
||||
// `switch_ty` is redundant, sanity-check that.
|
||||
assert_eq!(discr.layout.ty, switch_ty);
|
||||
let switch_ty = discr.layout.ty;
|
||||
let mut target_iter = targets.iter();
|
||||
if target_iter.len() == 1 {
|
||||
// If there are two targets (one conditional, one fallback), emit `br` instead of
|
||||
|
|
@ -1293,8 +1291,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
helper.funclet_br(self, bx, target, mergeable_succ())
|
||||
}
|
||||
|
||||
mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref targets } => {
|
||||
self.codegen_switchint_terminator(helper, bx, discr, switch_ty, targets);
|
||||
mir::TerminatorKind::SwitchInt { ref discr, ref targets } => {
|
||||
self.codegen_switchint_terminator(helper, bx, discr, targets);
|
||||
MergingSucc::False
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ use rustc_index::vec::IndexVec;
|
|||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||
use rustc_session::config::DebugInfo;
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
use rustc_span::{BytePos, Span};
|
||||
use rustc_target::abi::Abi;
|
||||
use rustc_target::abi::Size;
|
||||
use rustc_target::abi::{Abi, Size, VariantIdx};
|
||||
|
||||
use super::operand::{OperandRef, OperandValue};
|
||||
use super::place::PlaceRef;
|
||||
|
|
@ -76,6 +76,106 @@ impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
|
|||
}
|
||||
}
|
||||
|
||||
trait DebugInfoOffsetLocation<'tcx, Bx> {
|
||||
fn deref(&self, bx: &mut Bx) -> Self;
|
||||
fn layout(&self) -> TyAndLayout<'tcx>;
|
||||
fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self;
|
||||
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self;
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
|
||||
for PlaceRef<'tcx, Bx::Value>
|
||||
{
|
||||
fn deref(&self, bx: &mut Bx) -> Self {
|
||||
bx.load_operand(*self).deref(bx.cx())
|
||||
}
|
||||
|
||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
self.layout
|
||||
}
|
||||
|
||||
fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self {
|
||||
PlaceRef::project_field(*self, bx, field.index())
|
||||
}
|
||||
|
||||
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
|
||||
self.project_downcast(bx, variant)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
|
||||
for TyAndLayout<'tcx>
|
||||
{
|
||||
fn deref(&self, bx: &mut Bx) -> Self {
|
||||
bx.cx().layout_of(
|
||||
self.ty.builtin_deref(true).unwrap_or_else(|| bug!("cannot deref `{}`", self.ty)).ty,
|
||||
)
|
||||
}
|
||||
|
||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
*self
|
||||
}
|
||||
|
||||
fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self {
|
||||
self.field(bx.cx(), field.index())
|
||||
}
|
||||
|
||||
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
|
||||
self.for_variant(bx.cx(), variant)
|
||||
}
|
||||
}
|
||||
|
||||
struct DebugInfoOffset<T> {
|
||||
/// Offset from the `base` used to calculate the debuginfo offset.
|
||||
direct_offset: Size,
|
||||
/// Each offset in this vector indicates one level of indirection from the base or previous
|
||||
/// indirect offset plus a dereference.
|
||||
indirect_offsets: Vec<Size>,
|
||||
/// The final location debuginfo should point to.
|
||||
result: T,
|
||||
}
|
||||
|
||||
fn calculate_debuginfo_offset<
|
||||
'a,
|
||||
'tcx,
|
||||
Bx: BuilderMethods<'a, 'tcx>,
|
||||
L: DebugInfoOffsetLocation<'tcx, Bx>,
|
||||
>(
|
||||
bx: &mut Bx,
|
||||
local: mir::Local,
|
||||
var: &PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
|
||||
base: L,
|
||||
) -> DebugInfoOffset<L> {
|
||||
let mut direct_offset = Size::ZERO;
|
||||
// FIXME(eddyb) use smallvec here.
|
||||
let mut indirect_offsets = vec![];
|
||||
let mut place = base;
|
||||
|
||||
for elem in &var.projection[..] {
|
||||
match *elem {
|
||||
mir::ProjectionElem::Deref => {
|
||||
indirect_offsets.push(Size::ZERO);
|
||||
place = place.deref(bx);
|
||||
}
|
||||
mir::ProjectionElem::Field(field, _) => {
|
||||
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
|
||||
*offset += place.layout().fields.offset(field.index());
|
||||
place = place.project_field(bx, field);
|
||||
}
|
||||
mir::ProjectionElem::Downcast(_, variant) => {
|
||||
place = place.downcast(bx, variant);
|
||||
}
|
||||
_ => span_bug!(
|
||||
var.source_info.span,
|
||||
"unsupported var debuginfo place `{:?}`",
|
||||
mir::Place { local, projection: var.projection },
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
DebugInfoOffset { direct_offset, indirect_offsets, result: place }
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
|
||||
bx.set_span(source_info.span);
|
||||
|
|
@ -262,33 +362,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let Some(dbg_var) = var.dbg_var else { continue };
|
||||
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
|
||||
|
||||
let mut direct_offset = Size::ZERO;
|
||||
// FIXME(eddyb) use smallvec here.
|
||||
let mut indirect_offsets = vec![];
|
||||
let mut place = base;
|
||||
|
||||
for elem in &var.projection[..] {
|
||||
match *elem {
|
||||
mir::ProjectionElem::Deref => {
|
||||
indirect_offsets.push(Size::ZERO);
|
||||
place = bx.load_operand(place).deref(bx.cx());
|
||||
}
|
||||
mir::ProjectionElem::Field(field, _) => {
|
||||
let i = field.index();
|
||||
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
|
||||
*offset += place.layout.fields.offset(i);
|
||||
place = place.project_field(bx, i);
|
||||
}
|
||||
mir::ProjectionElem::Downcast(_, variant) => {
|
||||
place = place.project_downcast(bx, variant);
|
||||
}
|
||||
_ => span_bug!(
|
||||
var.source_info.span,
|
||||
"unsupported var debuginfo place `{:?}`",
|
||||
mir::Place { local, projection: var.projection },
|
||||
),
|
||||
}
|
||||
}
|
||||
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
|
||||
calculate_debuginfo_offset(bx, local, &var, base.layout);
|
||||
|
||||
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
|
||||
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
|
||||
|
|
@ -306,6 +381,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|| !matches!(&indirect_offsets[..], [Size::ZERO] | []));
|
||||
|
||||
if should_create_individual_allocas {
|
||||
let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
|
||||
calculate_debuginfo_offset(bx, local, &var, base);
|
||||
|
||||
// Create a variable which will be a pointer to the actual value
|
||||
let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut {
|
||||
mutbl: mir::Mutability::Mut,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> {
|
|||
pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> {
|
||||
fn set_type_metadata(&self, function: Self::Function, typeid: String);
|
||||
fn typeid_metadata(&self, typeid: String) -> Self::Value;
|
||||
fn set_kcfi_type_metadata(&self, function: Self::Function, typeid: u32);
|
||||
}
|
||||
|
||||
pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {
|
||||
|
|
|
|||
|
|
@ -123,14 +123,14 @@ impl<'tcx> ConstEvalErr<'tcx> {
|
|||
// Helper closure to print duplicated lines.
|
||||
let mut flush_last_line = |last_frame, times| {
|
||||
if let Some((line, span)) = last_frame {
|
||||
err.span_label(span, &line);
|
||||
err.span_note(span, &line);
|
||||
// Don't print [... additional calls ...] if the number of lines is small
|
||||
if times < 3 {
|
||||
for _ in 0..times {
|
||||
err.span_label(span, &line);
|
||||
err.span_note(span, &line);
|
||||
}
|
||||
} else {
|
||||
err.span_label(
|
||||
err.span_note(
|
||||
span,
|
||||
format!("[... {} additional calls {} ...]", times, &line),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use rustc_middle::ty::{
|
|||
};
|
||||
use rustc_mir_dataflow::storage::always_storage_live_locals;
|
||||
use rustc_session::Limit;
|
||||
use rustc_span::{Pos, Span};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout};
|
||||
|
||||
use super::{
|
||||
|
|
@ -256,25 +256,13 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> {
|
|||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data
|
||||
== DefPathData::ClosureExpr
|
||||
{
|
||||
write!(f, "inside closure")?;
|
||||
write!(f, "inside closure")
|
||||
} else {
|
||||
// Note: this triggers a `good_path_bug` state, which means that if we ever get here
|
||||
// we must emit a diagnostic. We should never display a `FrameInfo` unless we
|
||||
// actually want to emit a warning or error to the user.
|
||||
write!(f, "inside `{}`", self.instance)?;
|
||||
write!(f, "inside `{}`", self.instance)
|
||||
}
|
||||
if !self.span.is_dummy() {
|
||||
let sm = tcx.sess.source_map();
|
||||
let lo = sm.lookup_char_pos(self.span.lo());
|
||||
write!(
|
||||
f,
|
||||
" at {}:{}:{}",
|
||||
sm.filename_for_diagnostics(&lo.file.name),
|
||||
lo.line,
|
||||
lo.col.to_usize() + 1
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,10 +29,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
|
||||
Goto { target } => self.go_to_block(target),
|
||||
|
||||
SwitchInt { ref discr, ref targets, switch_ty } => {
|
||||
SwitchInt { ref discr, ref targets } => {
|
||||
let discr = self.read_immediate(&self.eval_operand(discr, None)?)?;
|
||||
trace!("SwitchInt({:?})", *discr);
|
||||
assert_eq!(discr.layout.ty, switch_ty);
|
||||
|
||||
// Branch to the `otherwise` case by default, if no match is found.
|
||||
let mut target_block = targets.otherwise();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_infer::traits::Reveal;
|
||||
use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
|
||||
use rustc_middle::mir::visit::{PlaceContext, Visitor};
|
||||
|
|
@ -44,8 +45,11 @@ impl<'tcx> MirPass<'tcx> for Validator {
|
|||
return;
|
||||
}
|
||||
let def_id = body.source.def_id();
|
||||
let param_env = tcx.param_env(def_id);
|
||||
let mir_phase = self.mir_phase;
|
||||
let param_env = match mir_phase.reveal() {
|
||||
Reveal::UserFacing => tcx.param_env(def_id),
|
||||
Reveal::All => tcx.param_env_reveal_all_normalized(def_id),
|
||||
};
|
||||
|
||||
let always_live_locals = always_storage_live_locals(body);
|
||||
let storage_liveness = MaybeStorageLive::new(always_live_locals)
|
||||
|
|
@ -682,17 +686,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
TerminatorKind::Goto { target } => {
|
||||
self.check_edge(location, *target, EdgeKind::Normal);
|
||||
}
|
||||
TerminatorKind::SwitchInt { targets, switch_ty, discr } => {
|
||||
let ty = discr.ty(&self.body.local_decls, self.tcx);
|
||||
if ty != *switch_ty {
|
||||
self.fail(
|
||||
location,
|
||||
format!(
|
||||
"encountered `SwitchInt` terminator with type mismatch: {:?} != {:?}",
|
||||
ty, switch_ty,
|
||||
),
|
||||
);
|
||||
}
|
||||
TerminatorKind::SwitchInt { targets, discr } => {
|
||||
let switch_ty = discr.ty(&self.body.local_decls, self.tcx);
|
||||
|
||||
let target_width = self.tcx.sess.target.pointer_width;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,3 +20,110 @@ expand_var_still_repeating =
|
|||
variable '{$ident}' is still repeating at this depth
|
||||
|
||||
expand_meta_var_dif_seq_matchers = {$msg}
|
||||
|
||||
expand_macro_const_stability =
|
||||
macros cannot have const stability attributes
|
||||
.label = invalid const stability attribute
|
||||
.label2 = const stability attribute affects this macro
|
||||
|
||||
expand_macro_body_stability =
|
||||
macros cannot have body stability attributes
|
||||
.label = invalid body stability attribute
|
||||
.label2 = body stability attribute affects this macro
|
||||
|
||||
expand_resolve_relative_path =
|
||||
cannot resolve relative path in non-file source `{$path}`
|
||||
|
||||
expand_attr_no_arguments =
|
||||
attribute must have either one or two arguments
|
||||
|
||||
expand_not_a_meta_item =
|
||||
not a meta item
|
||||
|
||||
expand_only_one_word =
|
||||
must only be one word
|
||||
|
||||
expand_cannot_be_name_of_macro =
|
||||
`{$trait_ident}` cannot be a name of {$macro_type} macro
|
||||
|
||||
expand_arg_not_attributes =
|
||||
second argument must be `attributes`
|
||||
|
||||
expand_attributes_wrong_form =
|
||||
attribute must be of form: `attributes(foo, bar)`
|
||||
|
||||
expand_attribute_meta_item =
|
||||
attribute must be a meta item, not a literal
|
||||
|
||||
expand_attribute_single_word =
|
||||
attribute must only be a single word
|
||||
|
||||
expand_helper_attribute_name_invalid =
|
||||
`{$name}` cannot be a name of derive helper attribute
|
||||
|
||||
expand_expected_comma_in_list =
|
||||
expected token: `,`
|
||||
|
||||
expand_only_one_argument =
|
||||
{$name} takes 1 argument
|
||||
|
||||
expand_takes_no_arguments =
|
||||
{$name} takes no arguments
|
||||
|
||||
expand_feature_included_in_edition =
|
||||
the feature `{$feature}` is included in the Rust {$edition} edition
|
||||
|
||||
expand_feature_removed =
|
||||
feature has been removed
|
||||
.label = feature has been removed
|
||||
.reason = {$reason}
|
||||
|
||||
expand_feature_not_allowed =
|
||||
the feature `{$name}` is not in the list of allowed features
|
||||
|
||||
expand_recursion_limit_reached =
|
||||
recursion limit reached while expanding `{$descr}`
|
||||
.help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`)
|
||||
|
||||
expand_malformed_feature_attribute =
|
||||
malformed `feature` attribute input
|
||||
.expected = expected just one word
|
||||
|
||||
expand_remove_expr_not_supported =
|
||||
removing an expression is not supported in this position
|
||||
|
||||
expand_invalid_cfg_no_parens = `cfg` is not followed by parentheses
|
||||
expand_invalid_cfg_no_predicate = `cfg` predicate is not specified
|
||||
expand_invalid_cfg_multiple_predicates = multiple `cfg` predicates are specified
|
||||
expand_invalid_cfg_predicate_literal = `cfg` predicate key cannot be a literal
|
||||
expand_invalid_cfg_expected_syntax = expected syntax is
|
||||
|
||||
expand_wrong_fragment_kind =
|
||||
non-{$kind} macro in {$kind} position: {$name}
|
||||
|
||||
expand_unsupported_key_value =
|
||||
key-value macro attributes are not supported
|
||||
|
||||
expand_incomplete_parse =
|
||||
macro expansion ignores token `{$token}` and any following
|
||||
.label = caused by the macro expansion here
|
||||
.note = the usage of `{$macro_path}!` is likely invalid in {$kind_name} context
|
||||
.suggestion_add_semi = you might be missing a semicolon here
|
||||
|
||||
expand_remove_node_not_supported =
|
||||
removing {$descr} is not supported in this position
|
||||
|
||||
expand_module_circular =
|
||||
circular modules: {$modules}
|
||||
|
||||
expand_module_in_block =
|
||||
cannot declare a non-inline module inside a block unless it has a path attribute
|
||||
.note = maybe `use` the module `{$name}` instead of redeclaring it
|
||||
|
||||
expand_module_file_not_found =
|
||||
file not found for module `{$name}`
|
||||
.help = to create the module `{$name}`, create file "{$default_path}" or "{$secondary_path}"
|
||||
|
||||
expand_module_multiple_candidates =
|
||||
file for module `{$name}` found at both "{$default_path}" and "{$secondary_path}"
|
||||
.help = delete or rename one of them to remove the ambiguity
|
||||
|
|
|
|||
|
|
@ -152,6 +152,12 @@ impl IntoDiagnosticArg for ast::Path {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for &ast::Path {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(self)))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for ast::token::Token {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
DiagnosticArgValue::Str(pprust::token_to_string(&self))
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use rustc_lint_defs::pluralize;
|
|||
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_error_messages::FluentArgs;
|
||||
use rustc_error_messages::{FluentArgs, SpanLabel};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::{max, min, Reverse};
|
||||
|
|
@ -773,6 +773,7 @@ impl EmitterWriter {
|
|||
draw_col_separator_no_space(buffer, line_offset, width_offset - 2);
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn render_source_line(
|
||||
&self,
|
||||
buffer: &mut StyledBuffer,
|
||||
|
|
@ -804,6 +805,7 @@ impl EmitterWriter {
|
|||
Some(s) => normalize_whitespace(&s),
|
||||
None => return Vec::new(),
|
||||
};
|
||||
trace!(?source_string);
|
||||
|
||||
let line_offset = buffer.num_lines();
|
||||
|
||||
|
|
@ -1323,6 +1325,7 @@ impl EmitterWriter {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, args), ret)]
|
||||
fn emit_message_default(
|
||||
&mut self,
|
||||
msp: &MultiSpan,
|
||||
|
|
@ -1384,22 +1387,15 @@ impl EmitterWriter {
|
|||
}
|
||||
}
|
||||
let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
|
||||
trace!("{annotated_files:#?}");
|
||||
|
||||
// Make sure our primary file comes first
|
||||
let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) =
|
||||
(self.sm.as_ref(), msp.primary_span().as_ref())
|
||||
{
|
||||
if !primary_span.is_dummy() {
|
||||
(sm.lookup_char_pos(primary_span.lo()), sm)
|
||||
} else {
|
||||
emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
let primary_span = msp.primary_span().unwrap_or_default();
|
||||
let (Some(sm), false) = (self.sm.as_ref(), primary_span.is_dummy()) else {
|
||||
// If we don't have span information, emit and exit
|
||||
emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
|
||||
return Ok(());
|
||||
return emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message);
|
||||
};
|
||||
let primary_lo = sm.lookup_char_pos(primary_span.lo());
|
||||
if let Ok(pos) =
|
||||
annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
|
||||
{
|
||||
|
|
@ -1410,6 +1406,54 @@ impl EmitterWriter {
|
|||
for annotated_file in annotated_files {
|
||||
// we can't annotate anything if the source is unavailable.
|
||||
if !sm.ensure_source_file_source_present(annotated_file.file.clone()) {
|
||||
if !self.short_message {
|
||||
// We'll just print an unannotated message.
|
||||
for (annotation_id, line) in annotated_file.lines.into_iter().enumerate() {
|
||||
let mut annotations = line.annotations.clone();
|
||||
annotations.sort_by_key(|a| Reverse(a.start_col));
|
||||
let mut line_idx = buffer.num_lines();
|
||||
buffer.append(
|
||||
line_idx,
|
||||
&format!(
|
||||
"{}:{}:{}",
|
||||
sm.filename_for_diagnostics(&annotated_file.file.name),
|
||||
sm.doctest_offset_line(&annotated_file.file.name, line.line_index),
|
||||
annotations[0].start_col + 1,
|
||||
),
|
||||
Style::LineAndColumn,
|
||||
);
|
||||
if annotation_id == 0 {
|
||||
buffer.prepend(line_idx, "--> ", Style::LineNumber);
|
||||
for _ in 0..max_line_num_len {
|
||||
buffer.prepend(line_idx, " ", Style::NoStyle);
|
||||
}
|
||||
line_idx += 1;
|
||||
};
|
||||
for (i, annotation) in annotations.into_iter().enumerate() {
|
||||
if let Some(label) = &annotation.label {
|
||||
let style = if annotation.is_primary {
|
||||
Style::LabelPrimary
|
||||
} else {
|
||||
Style::LabelSecondary
|
||||
};
|
||||
if annotation_id == 0 {
|
||||
buffer.prepend(line_idx, " |", Style::LineNumber);
|
||||
for _ in 0..max_line_num_len {
|
||||
buffer.prepend(line_idx, " ", Style::NoStyle);
|
||||
}
|
||||
line_idx += 1;
|
||||
buffer.append(line_idx + i, " = note: ", style);
|
||||
for _ in 0..max_line_num_len {
|
||||
buffer.prepend(line_idx, " ", Style::NoStyle);
|
||||
}
|
||||
} else {
|
||||
buffer.append(line_idx + i, ": ", style);
|
||||
}
|
||||
buffer.append(line_idx + i, label, style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1656,6 +1700,7 @@ impl EmitterWriter {
|
|||
multilines.extend(&to_add);
|
||||
}
|
||||
}
|
||||
trace!("buffer: {:#?}", buffer.render());
|
||||
}
|
||||
|
||||
if let Some(tracked) = emitted_at {
|
||||
|
|
@ -1979,6 +2024,7 @@ impl EmitterWriter {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, args, code, children, suggestions))]
|
||||
fn emit_messages_default(
|
||||
&mut self,
|
||||
level: &Level,
|
||||
|
|
@ -2209,46 +2255,28 @@ impl FileWithAnnotatedLines {
|
|||
let mut multiline_annotations = vec![];
|
||||
|
||||
if let Some(ref sm) = emitter.source_map() {
|
||||
for span_label in msp.span_labels() {
|
||||
let fixup_lo_hi = |span: Span| {
|
||||
let lo = sm.lookup_char_pos(span.lo());
|
||||
let mut hi = sm.lookup_char_pos(span.hi());
|
||||
|
||||
// Watch out for "empty spans". If we get a span like 6..6, we
|
||||
// want to just display a `^` at 6, so convert that to
|
||||
// 6..7. This is degenerate input, but it's best to degrade
|
||||
// gracefully -- and the parser likes to supply a span like
|
||||
// that for EOF, in particular.
|
||||
|
||||
if lo.col_display == hi.col_display && lo.line == hi.line {
|
||||
hi.col_display += 1;
|
||||
}
|
||||
(lo, hi)
|
||||
for SpanLabel { span, is_primary, label } in msp.span_labels() {
|
||||
// If we don't have a useful span, pick the primary span if that exists.
|
||||
// Worst case we'll just print an error at the top of the main file.
|
||||
let span = match (span.is_dummy(), msp.primary_span()) {
|
||||
(_, None) | (false, _) => span,
|
||||
(true, Some(span)) => span,
|
||||
};
|
||||
|
||||
if span_label.span.is_dummy() {
|
||||
if let Some(span) = msp.primary_span() {
|
||||
// if we don't know where to render the annotation, emit it as a note
|
||||
// on the primary span.
|
||||
let lo = sm.lookup_char_pos(span.lo());
|
||||
let mut hi = sm.lookup_char_pos(span.hi());
|
||||
|
||||
let (lo, hi) = fixup_lo_hi(span);
|
||||
// Watch out for "empty spans". If we get a span like 6..6, we
|
||||
// want to just display a `^` at 6, so convert that to
|
||||
// 6..7. This is degenerate input, but it's best to degrade
|
||||
// gracefully -- and the parser likes to supply a span like
|
||||
// that for EOF, in particular.
|
||||
|
||||
let ann = Annotation {
|
||||
start_col: lo.col_display,
|
||||
end_col: hi.col_display,
|
||||
is_primary: span_label.is_primary,
|
||||
label: span_label
|
||||
.label
|
||||
.as_ref()
|
||||
.map(|m| emitter.translate_message(m, args).to_string()),
|
||||
annotation_type: AnnotationType::Singleline,
|
||||
};
|
||||
add_annotation_to_file(&mut output, lo.file, lo.line, ann);
|
||||
}
|
||||
continue;
|
||||
if lo.col_display == hi.col_display && lo.line == hi.line {
|
||||
hi.col_display += 1;
|
||||
}
|
||||
|
||||
let (lo, hi) = fixup_lo_hi(span_label.span);
|
||||
let label = label.as_ref().map(|m| emitter.translate_message(m, args).to_string());
|
||||
|
||||
if lo.line != hi.line {
|
||||
let ml = MultilineAnnotation {
|
||||
|
|
@ -2257,11 +2285,8 @@ impl FileWithAnnotatedLines {
|
|||
line_end: hi.line,
|
||||
start_col: lo.col_display,
|
||||
end_col: hi.col_display,
|
||||
is_primary: span_label.is_primary,
|
||||
label: span_label
|
||||
.label
|
||||
.as_ref()
|
||||
.map(|m| emitter.translate_message(m, args).to_string()),
|
||||
is_primary,
|
||||
label,
|
||||
overlaps_exactly: false,
|
||||
};
|
||||
multiline_annotations.push((lo.file, ml));
|
||||
|
|
@ -2269,11 +2294,8 @@ impl FileWithAnnotatedLines {
|
|||
let ann = Annotation {
|
||||
start_col: lo.col_display,
|
||||
end_col: hi.col_display,
|
||||
is_primary: span_label.is_primary,
|
||||
label: span_label
|
||||
.label
|
||||
.as_ref()
|
||||
.map(|m| emitter.translate_message(m, args).to_string()),
|
||||
is_primary,
|
||||
label,
|
||||
annotation_type: AnnotationType::Singleline,
|
||||
};
|
||||
add_annotation_to_file(&mut output, lo.file, lo.line, ann);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
|
||||
use crate::errors::{
|
||||
ArgumentNotAttributes, AttrNoArguments, AttributeMetaItem, AttributeSingleWord,
|
||||
AttributesWrongForm, CannotBeNameOfMacro, ExpectedCommaInList, HelperAttributeNameInvalid,
|
||||
MacroBodyStability, MacroConstStability, NotAMetaItem, OnlyOneArgument, OnlyOneWord,
|
||||
ResolveRelativePath, TakesNoArguments,
|
||||
};
|
||||
use crate::expand::{self, AstFragment, Invocation};
|
||||
use crate::module::DirOwnership;
|
||||
|
||||
|
|
@ -789,26 +797,16 @@ impl SyntaxExtension {
|
|||
.unwrap_or_else(|| (None, helper_attrs));
|
||||
let (stability, const_stability, body_stability) = attr::find_stability(&sess, attrs, span);
|
||||
if let Some((_, sp)) = const_stability {
|
||||
sess.parse_sess
|
||||
.span_diagnostic
|
||||
.struct_span_err(sp, "macros cannot have const stability attributes")
|
||||
.span_label(sp, "invalid const stability attribute")
|
||||
.span_label(
|
||||
sess.source_map().guess_head_span(span),
|
||||
"const stability attribute affects this macro",
|
||||
)
|
||||
.emit();
|
||||
sess.emit_err(MacroConstStability {
|
||||
span: sp,
|
||||
head_span: sess.source_map().guess_head_span(span),
|
||||
});
|
||||
}
|
||||
if let Some((_, sp)) = body_stability {
|
||||
sess.parse_sess
|
||||
.span_diagnostic
|
||||
.struct_span_err(sp, "macros cannot have body stability attributes")
|
||||
.span_label(sp, "invalid body stability attribute")
|
||||
.span_label(
|
||||
sess.source_map().guess_head_span(span),
|
||||
"body stability attribute affects this macro",
|
||||
)
|
||||
.emit();
|
||||
sess.emit_err(MacroBodyStability {
|
||||
span: sp,
|
||||
head_span: sess.source_map().guess_head_span(span),
|
||||
});
|
||||
}
|
||||
|
||||
SyntaxExtension {
|
||||
|
|
@ -1200,13 +1198,11 @@ pub fn resolve_path(
|
|||
.expect("attempting to resolve a file path in an external file"),
|
||||
FileName::DocTest(path, _) => path,
|
||||
other => {
|
||||
return Err(parse_sess.span_diagnostic.struct_span_err(
|
||||
return Err(ResolveRelativePath {
|
||||
span,
|
||||
&format!(
|
||||
"cannot resolve relative path in non-file source `{}`",
|
||||
parse_sess.source_map().filename_for_diagnostics(&other)
|
||||
),
|
||||
));
|
||||
path: parse_sess.source_map().filename_for_diagnostics(&other).to_string(),
|
||||
}
|
||||
.into_diagnostic(&parse_sess.span_diagnostic));
|
||||
}
|
||||
};
|
||||
result.pop();
|
||||
|
|
@ -1222,6 +1218,8 @@ pub fn resolve_path(
|
|||
/// The returned bool indicates whether an applicable suggestion has already been
|
||||
/// added to the diagnostic to avoid emitting multiple suggestions. `Err(None)`
|
||||
/// indicates that an ast error was encountered.
|
||||
// FIXME(Nilstrieb) Make this function setup translatable
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
pub fn expr_to_spanned_string<'a>(
|
||||
cx: &'a mut ExtCtxt<'_>,
|
||||
expr: P<ast::Expr>,
|
||||
|
|
@ -1280,9 +1278,9 @@ pub fn expr_to_string(
|
|||
/// compilation should call
|
||||
/// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be
|
||||
/// done as rarely as possible).
|
||||
pub fn check_zero_tts(cx: &ExtCtxt<'_>, sp: Span, tts: TokenStream, name: &str) {
|
||||
pub fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) {
|
||||
if !tts.is_empty() {
|
||||
cx.span_err(sp, &format!("{} takes no arguments", name));
|
||||
cx.emit_err(TakesNoArguments { span, name });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1304,31 +1302,27 @@ pub fn parse_expr(p: &mut parser::Parser<'_>) -> Option<P<ast::Expr>> {
|
|||
/// expect exactly one string literal, or emit an error and return `None`.
|
||||
pub fn get_single_str_from_tts(
|
||||
cx: &mut ExtCtxt<'_>,
|
||||
sp: Span,
|
||||
span: Span,
|
||||
tts: TokenStream,
|
||||
name: &str,
|
||||
) -> Option<Symbol> {
|
||||
let mut p = cx.new_parser_from_tts(tts);
|
||||
if p.token == token::Eof {
|
||||
cx.span_err(sp, &format!("{} takes 1 argument", name));
|
||||
cx.emit_err(OnlyOneArgument { span, name });
|
||||
return None;
|
||||
}
|
||||
let ret = parse_expr(&mut p)?;
|
||||
let _ = p.eat(&token::Comma);
|
||||
|
||||
if p.token != token::Eof {
|
||||
cx.span_err(sp, &format!("{} takes 1 argument", name));
|
||||
cx.emit_err(OnlyOneArgument { span, name });
|
||||
}
|
||||
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s)
|
||||
}
|
||||
|
||||
/// Extracts comma-separated expressions from `tts`.
|
||||
/// On error, emit it, and return `None`.
|
||||
pub fn get_exprs_from_tts(
|
||||
cx: &mut ExtCtxt<'_>,
|
||||
sp: Span,
|
||||
tts: TokenStream,
|
||||
) -> Option<Vec<P<ast::Expr>>> {
|
||||
pub fn get_exprs_from_tts(cx: &mut ExtCtxt<'_>, tts: TokenStream) -> Option<Vec<P<ast::Expr>>> {
|
||||
let mut p = cx.new_parser_from_tts(tts);
|
||||
let mut es = Vec::new();
|
||||
while p.token != token::Eof {
|
||||
|
|
@ -1343,7 +1337,7 @@ pub fn get_exprs_from_tts(
|
|||
continue;
|
||||
}
|
||||
if p.token != token::Eof {
|
||||
cx.span_err(sp, "expected token: `,`");
|
||||
cx.emit_err(ExpectedCommaInList { span: p.token.span });
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
|
@ -1353,64 +1347,58 @@ pub fn get_exprs_from_tts(
|
|||
pub fn parse_macro_name_and_helper_attrs(
|
||||
diag: &rustc_errors::Handler,
|
||||
attr: &Attribute,
|
||||
descr: &str,
|
||||
macro_type: &str,
|
||||
) -> Option<(Symbol, Vec<Symbol>)> {
|
||||
// Once we've located the `#[proc_macro_derive]` attribute, verify
|
||||
// that it's of the form `#[proc_macro_derive(Foo)]` or
|
||||
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
|
||||
let list = attr.meta_item_list()?;
|
||||
if list.len() != 1 && list.len() != 2 {
|
||||
diag.span_err(attr.span, "attribute must have either one or two arguments");
|
||||
diag.emit_err(AttrNoArguments { span: attr.span });
|
||||
return None;
|
||||
}
|
||||
let Some(trait_attr) = list[0].meta_item() else {
|
||||
diag.span_err(list[0].span(), "not a meta item");
|
||||
diag.emit_err(NotAMetaItem {span: list[0].span()});
|
||||
return None;
|
||||
};
|
||||
let trait_ident = match trait_attr.ident() {
|
||||
Some(trait_ident) if trait_attr.is_word() => trait_ident,
|
||||
_ => {
|
||||
diag.span_err(trait_attr.span, "must only be one word");
|
||||
diag.emit_err(OnlyOneWord { span: trait_attr.span });
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if !trait_ident.name.can_be_raw() {
|
||||
diag.span_err(
|
||||
trait_attr.span,
|
||||
&format!("`{}` cannot be a name of {} macro", trait_ident, descr),
|
||||
);
|
||||
diag.emit_err(CannotBeNameOfMacro { span: trait_attr.span, trait_ident, macro_type });
|
||||
}
|
||||
|
||||
let attributes_attr = list.get(1);
|
||||
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
|
||||
if !attr.has_name(sym::attributes) {
|
||||
diag.span_err(attr.span(), "second argument must be `attributes`");
|
||||
diag.emit_err(ArgumentNotAttributes { span: attr.span() });
|
||||
}
|
||||
attr.meta_item_list()
|
||||
.unwrap_or_else(|| {
|
||||
diag.span_err(attr.span(), "attribute must be of form: `attributes(foo, bar)`");
|
||||
diag.emit_err(AttributesWrongForm { span: attr.span() });
|
||||
&[]
|
||||
})
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
let Some(attr) = attr.meta_item() else {
|
||||
diag.span_err(attr.span(), "not a meta item");
|
||||
diag.emit_err(AttributeMetaItem { span: attr.span() });
|
||||
return None;
|
||||
};
|
||||
|
||||
let ident = match attr.ident() {
|
||||
Some(ident) if attr.is_word() => ident,
|
||||
_ => {
|
||||
diag.span_err(attr.span, "must only be one word");
|
||||
diag.emit_err(AttributeSingleWord { span: attr.span });
|
||||
return None;
|
||||
}
|
||||
};
|
||||
if !ident.name.can_be_raw() {
|
||||
diag.span_err(
|
||||
attr.span,
|
||||
&format!("`{}` cannot be a name of derive helper attribute", ident),
|
||||
);
|
||||
diag.emit_err(HelperAttributeNameInvalid { span: attr.span, name: ident });
|
||||
}
|
||||
|
||||
Some(ident.name)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
//! Conditional compilation stripping.
|
||||
|
||||
use crate::errors::{
|
||||
FeatureIncludedInEdition, FeatureNotAllowed, FeatureRemoved, FeatureRemovedReason, InvalidCfg,
|
||||
MalformedFeatureAttribute, MalformedFeatureAttributeHelp, RemoveExprNotSupported,
|
||||
};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{Delimiter, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree};
|
||||
|
|
@ -10,7 +14,6 @@ use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem
|
|||
use rustc_attr as attr;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::map_in_place::MapInPlace;
|
||||
use rustc_errors::{error_code, struct_span_err, Applicability, Handler};
|
||||
use rustc_feature::{Feature, Features, State as FeatureState};
|
||||
use rustc_feature::{
|
||||
ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
|
||||
|
|
@ -33,18 +36,12 @@ pub struct StripUnconfigured<'a> {
|
|||
pub lint_node_id: NodeId,
|
||||
}
|
||||
|
||||
fn get_features(
|
||||
sess: &Session,
|
||||
span_handler: &Handler,
|
||||
krate_attrs: &[ast::Attribute],
|
||||
) -> Features {
|
||||
fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
|
||||
let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed");
|
||||
err.span_label(span, "feature has been removed");
|
||||
if let Some(reason) = reason {
|
||||
err.note(reason);
|
||||
}
|
||||
err.emit();
|
||||
fn get_features(sess: &Session, krate_attrs: &[ast::Attribute]) -> Features {
|
||||
fn feature_removed(sess: &Session, span: Span, reason: Option<&str>) {
|
||||
sess.emit_err(FeatureRemoved {
|
||||
span,
|
||||
reason: reason.map(|reason| FeatureRemovedReason { reason }),
|
||||
});
|
||||
}
|
||||
|
||||
fn active_features_up_to(edition: Edition) -> impl Iterator<Item = &'static Feature> {
|
||||
|
|
@ -117,34 +114,34 @@ fn get_features(
|
|||
continue;
|
||||
};
|
||||
|
||||
let bad_input = |span| {
|
||||
struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input")
|
||||
};
|
||||
|
||||
for mi in list {
|
||||
let name = match mi.ident() {
|
||||
Some(ident) if mi.is_word() => ident.name,
|
||||
Some(ident) => {
|
||||
bad_input(mi.span())
|
||||
.span_suggestion(
|
||||
mi.span(),
|
||||
"expected just one word",
|
||||
ident.name,
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit();
|
||||
sess.emit_err(MalformedFeatureAttribute {
|
||||
span: mi.span(),
|
||||
help: MalformedFeatureAttributeHelp::Suggestion {
|
||||
span: mi.span(),
|
||||
suggestion: ident.name,
|
||||
},
|
||||
});
|
||||
continue;
|
||||
}
|
||||
None => {
|
||||
bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit();
|
||||
sess.emit_err(MalformedFeatureAttribute {
|
||||
span: mi.span(),
|
||||
help: MalformedFeatureAttributeHelp::Label { span: mi.span() },
|
||||
});
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(edition) = edition_enabled_features.get(&name) {
|
||||
let msg =
|
||||
&format!("the feature `{}` is included in the Rust {} edition", name, edition);
|
||||
span_handler.struct_span_warn_with_code(mi.span(), msg, error_code!(E0705)).emit();
|
||||
if let Some(&edition) = edition_enabled_features.get(&name) {
|
||||
sess.emit_warning(FeatureIncludedInEdition {
|
||||
span: mi.span(),
|
||||
feature: name,
|
||||
edition,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +156,7 @@ fn get_features(
|
|||
if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } =
|
||||
state
|
||||
{
|
||||
feature_removed(span_handler, mi.span(), *reason);
|
||||
feature_removed(sess, mi.span(), *reason);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -173,14 +170,7 @@ fn get_features(
|
|||
|
||||
if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() {
|
||||
if allowed.iter().all(|f| name.as_str() != f) {
|
||||
struct_span_err!(
|
||||
span_handler,
|
||||
mi.span(),
|
||||
E0725,
|
||||
"the feature `{}` is not in the list of allowed features",
|
||||
name
|
||||
)
|
||||
.emit();
|
||||
sess.emit_err(FeatureNotAllowed { span: mi.span(), name });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -221,7 +211,7 @@ pub fn features(
|
|||
}
|
||||
Some(attrs) => {
|
||||
krate.attrs = attrs;
|
||||
let features = get_features(sess, diag, &krate.attrs);
|
||||
let features = get_features(sess, &krate.attrs);
|
||||
if err_count == diag.err_count() {
|
||||
// Avoid reconfiguring malformed `cfg_attr`s.
|
||||
strip_unconfigured.features = Some(&features);
|
||||
|
|
@ -503,8 +493,7 @@ impl<'a> StripUnconfigured<'a> {
|
|||
// N.B., this is intentionally not part of the visit_expr() function
|
||||
// in order for filter_map_expr() to be able to avoid this check
|
||||
if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(*a)) {
|
||||
let msg = "removing an expression is not supported in this position";
|
||||
self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg);
|
||||
self.sess.emit_err(RemoveExprNotSupported { span: attr.span });
|
||||
}
|
||||
|
||||
self.process_cfg_attrs(expr);
|
||||
|
|
@ -513,27 +502,26 @@ impl<'a> StripUnconfigured<'a> {
|
|||
}
|
||||
|
||||
pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItem> {
|
||||
let error = |span, msg, suggestion: &str| {
|
||||
let mut err = sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
|
||||
if !suggestion.is_empty() {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"expected syntax is",
|
||||
suggestion,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
err.emit();
|
||||
None
|
||||
};
|
||||
let span = meta_item.span;
|
||||
match meta_item.meta_item_list() {
|
||||
None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
|
||||
Some([]) => error(span, "`cfg` predicate is not specified", ""),
|
||||
Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
|
||||
None => {
|
||||
sess.emit_err(InvalidCfg::NotFollowedByParens { span });
|
||||
None
|
||||
}
|
||||
Some([]) => {
|
||||
sess.emit_err(InvalidCfg::NoPredicate { span });
|
||||
None
|
||||
}
|
||||
Some([_, .., l]) => {
|
||||
sess.emit_err(InvalidCfg::MultiplePredicates { span: l.span() });
|
||||
None
|
||||
}
|
||||
Some([single]) => match single.meta_item() {
|
||||
Some(meta_item) => Some(meta_item),
|
||||
None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
|
||||
None => {
|
||||
sess.emit_err(InvalidCfg::PredicateLiteral { span: single.span() });
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
use rustc_ast::ast;
|
||||
use rustc_macros::Diagnostic;
|
||||
use rustc_span::symbol::MacroRulesNormalizedIdent;
|
||||
use rustc_span::Span;
|
||||
use rustc_session::Limit;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent};
|
||||
use rustc_span::{Span, Symbol};
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_expr_repeat_no_syntax_vars)]
|
||||
|
|
@ -46,3 +50,321 @@ pub(crate) struct MetaVarsDifSeqMatchers {
|
|||
pub span: Span,
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_resolve_relative_path)]
|
||||
pub(crate) struct ResolveRelativePath {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_macro_const_stability)]
|
||||
pub(crate) struct MacroConstStability {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[label(label2)]
|
||||
pub head_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_macro_body_stability)]
|
||||
pub(crate) struct MacroBodyStability {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[label(label2)]
|
||||
pub head_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attr_no_arguments)]
|
||||
pub(crate) struct AttrNoArguments {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_not_a_meta_item)]
|
||||
pub(crate) struct NotAMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_only_one_word)]
|
||||
pub(crate) struct OnlyOneWord {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_cannot_be_name_of_macro)]
|
||||
pub(crate) struct CannotBeNameOfMacro<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_ident: Ident,
|
||||
pub macro_type: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_arg_not_attributes)]
|
||||
pub(crate) struct ArgumentNotAttributes {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attributes_wrong_form)]
|
||||
pub(crate) struct AttributesWrongForm {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attribute_meta_item)]
|
||||
pub(crate) struct AttributeMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attribute_single_word)]
|
||||
pub(crate) struct AttributeSingleWord {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_helper_attribute_name_invalid)]
|
||||
pub(crate) struct HelperAttributeNameInvalid {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Ident,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_expected_comma_in_list)]
|
||||
pub(crate) struct ExpectedCommaInList {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_only_one_argument)]
|
||||
pub(crate) struct OnlyOneArgument<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_takes_no_arguments)]
|
||||
pub(crate) struct TakesNoArguments<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_feature_included_in_edition, code = "E0705")]
|
||||
pub(crate) struct FeatureIncludedInEdition {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub feature: Symbol,
|
||||
pub edition: Edition,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_feature_removed, code = "E0557")]
|
||||
pub(crate) struct FeatureRemoved<'a> {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub reason: Option<FeatureRemovedReason<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[note(reason)]
|
||||
pub(crate) struct FeatureRemovedReason<'a> {
|
||||
pub reason: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_feature_not_allowed, code = "E0725")]
|
||||
pub(crate) struct FeatureNotAllowed {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_recursion_limit_reached)]
|
||||
#[help]
|
||||
pub(crate) struct RecursionLimitReached<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub descr: String,
|
||||
pub suggested_limit: Limit,
|
||||
pub crate_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_malformed_feature_attribute, code = "E0556")]
|
||||
pub(crate) struct MalformedFeatureAttribute {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub help: MalformedFeatureAttributeHelp,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum MalformedFeatureAttributeHelp {
|
||||
#[label(expected)]
|
||||
Label {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[suggestion(expected, code = "{suggestion}", applicability = "maybe-incorrect")]
|
||||
Suggestion {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
suggestion: Symbol,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_remove_expr_not_supported)]
|
||||
pub(crate) struct RemoveExprNotSupported {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub(crate) enum InvalidCfg {
|
||||
#[diag(expand_invalid_cfg_no_parens)]
|
||||
NotFollowedByParens {
|
||||
#[primary_span]
|
||||
#[suggestion(
|
||||
expand_invalid_cfg_expected_syntax,
|
||||
code = "cfg(/* predicate */)",
|
||||
applicability = "has-placeholders"
|
||||
)]
|
||||
span: Span,
|
||||
},
|
||||
#[diag(expand_invalid_cfg_no_predicate)]
|
||||
NoPredicate {
|
||||
#[primary_span]
|
||||
#[suggestion(
|
||||
expand_invalid_cfg_expected_syntax,
|
||||
code = "cfg(/* predicate */)",
|
||||
applicability = "has-placeholders"
|
||||
)]
|
||||
span: Span,
|
||||
},
|
||||
#[diag(expand_invalid_cfg_multiple_predicates)]
|
||||
MultiplePredicates {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[diag(expand_invalid_cfg_predicate_literal)]
|
||||
PredicateLiteral {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_wrong_fragment_kind)]
|
||||
pub(crate) struct WrongFragmentKind<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub kind: &'a str,
|
||||
pub name: &'a ast::Path,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_unsupported_key_value)]
|
||||
pub(crate) struct UnsupportedKeyValue {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_incomplete_parse)]
|
||||
#[note]
|
||||
pub(crate) struct IncompleteParse<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub token: Cow<'a, str>,
|
||||
#[label]
|
||||
pub label_span: Span,
|
||||
pub macro_path: &'a ast::Path,
|
||||
pub kind_name: &'a str,
|
||||
|
||||
#[suggestion(
|
||||
suggestion_add_semi,
|
||||
style = "verbose",
|
||||
code = ";",
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
pub add_semicolon: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_remove_node_not_supported)]
|
||||
pub(crate) struct RemoveNodeNotSupported {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub descr: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_module_circular)]
|
||||
pub(crate) struct ModuleCircular {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub modules: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_module_in_block)]
|
||||
pub(crate) struct ModuleInBlock {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub name: Option<ModuleInBlockName>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[note(note)]
|
||||
pub(crate) struct ModuleInBlockName {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Ident,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_module_file_not_found, code = "E0583")]
|
||||
#[help]
|
||||
pub(crate) struct ModuleFileNotFound {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Ident,
|
||||
pub default_path: String,
|
||||
pub secondary_path: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_module_multiple_candidates, code = "E0761")]
|
||||
#[help]
|
||||
pub(crate) struct ModuleMultipleCandidates {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Ident,
|
||||
pub default_path: String,
|
||||
pub secondary_path: String,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
use crate::base::*;
|
||||
use crate::config::StripUnconfigured;
|
||||
use crate::errors::{
|
||||
IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported,
|
||||
UnsupportedKeyValue, WrongFragmentKind,
|
||||
};
|
||||
use crate::hygiene::SyntaxContext;
|
||||
use crate::mbe::diagnostics::annotate_err_with_kind;
|
||||
use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
|
||||
|
|
@ -18,7 +22,7 @@ use rustc_ast::{NestedMetaItem, NodeId, PatKind, StmtKind, TyKind};
|
|||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::map_in_place::MapInPlace;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{Applicability, PResult};
|
||||
use rustc_errors::PResult;
|
||||
use rustc_feature::Features;
|
||||
use rustc_parse::parser::{
|
||||
AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
|
||||
|
|
@ -606,29 +610,22 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
Limit(0) => Limit(2),
|
||||
limit => limit * 2,
|
||||
};
|
||||
self.cx
|
||||
.struct_span_err(
|
||||
expn_data.call_site,
|
||||
&format!("recursion limit reached while expanding `{}`", expn_data.kind.descr()),
|
||||
)
|
||||
.help(&format!(
|
||||
"consider increasing the recursion limit by adding a \
|
||||
`#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
|
||||
suggested_limit, self.cx.ecfg.crate_name,
|
||||
))
|
||||
.emit();
|
||||
|
||||
self.cx.emit_err(RecursionLimitReached {
|
||||
span: expn_data.call_site,
|
||||
descr: expn_data.kind.descr(),
|
||||
suggested_limit,
|
||||
crate_name: &self.cx.ecfg.crate_name,
|
||||
});
|
||||
|
||||
self.cx.trace_macros_diag();
|
||||
}
|
||||
|
||||
/// A macro's expansion does not fit in this fragment kind.
|
||||
/// For example, a non-type macro in a type position.
|
||||
fn error_wrong_fragment_kind(&mut self, kind: AstFragmentKind, mac: &ast::MacCall, span: Span) {
|
||||
let msg = format!(
|
||||
"non-{kind} macro in {kind} position: {path}",
|
||||
kind = kind.name(),
|
||||
path = pprust::path_to_string(&mac.path),
|
||||
);
|
||||
self.cx.span_err(span, &msg);
|
||||
self.cx.emit_err(WrongFragmentKind { span, kind: kind.name(), name: &mac.path });
|
||||
|
||||
self.cx.trace_macros_diag();
|
||||
}
|
||||
|
||||
|
|
@ -707,7 +704,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
};
|
||||
let attr_item = attr.unwrap_normal_item();
|
||||
if let AttrArgs::Eq(..) = attr_item.args {
|
||||
self.cx.span_err(span, "key-value macro attributes are not supported");
|
||||
self.cx.emit_err(UnsupportedKeyValue { span });
|
||||
}
|
||||
let inner_tokens = attr_item.args.inner_tokens();
|
||||
let Ok(tok_result) = expander.expand(self.cx, span, inner_tokens, tokens) else {
|
||||
|
|
@ -729,9 +726,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
}
|
||||
};
|
||||
if fragment_kind == AstFragmentKind::Expr && items.is_empty() {
|
||||
let msg =
|
||||
"removing an expression is not supported in this position";
|
||||
self.cx.span_err(span, msg);
|
||||
self.cx.emit_err(RemoveExprNotSupported { span });
|
||||
fragment_kind.dummy(span)
|
||||
} else {
|
||||
fragment_kind.expect_from_annotatables(items)
|
||||
|
|
@ -939,38 +934,32 @@ pub fn parse_ast_fragment<'a>(
|
|||
}
|
||||
|
||||
pub fn ensure_complete_parse<'a>(
|
||||
this: &mut Parser<'a>,
|
||||
parser: &mut Parser<'a>,
|
||||
macro_path: &ast::Path,
|
||||
kind_name: &str,
|
||||
span: Span,
|
||||
) {
|
||||
if this.token != token::Eof {
|
||||
let token = pprust::token_to_string(&this.token);
|
||||
let msg = format!("macro expansion ignores token `{}` and any following", token);
|
||||
if parser.token != token::Eof {
|
||||
let token = pprust::token_to_string(&parser.token);
|
||||
// Avoid emitting backtrace info twice.
|
||||
let def_site_span = this.token.span.with_ctxt(SyntaxContext::root());
|
||||
let mut err = this.struct_span_err(def_site_span, &msg);
|
||||
err.span_label(span, "caused by the macro expansion here");
|
||||
let msg = format!(
|
||||
"the usage of `{}!` is likely invalid in {} context",
|
||||
pprust::path_to_string(macro_path),
|
||||
kind_name,
|
||||
);
|
||||
err.note(&msg);
|
||||
let def_site_span = parser.token.span.with_ctxt(SyntaxContext::root());
|
||||
|
||||
let semi_span = this.sess.source_map().next_point(span);
|
||||
match this.sess.source_map().span_to_snippet(semi_span) {
|
||||
let semi_span = parser.sess.source_map().next_point(span);
|
||||
let add_semicolon = match parser.sess.source_map().span_to_snippet(semi_span) {
|
||||
Ok(ref snippet) if &snippet[..] != ";" && kind_name == "expression" => {
|
||||
err.span_suggestion(
|
||||
span.shrink_to_hi(),
|
||||
"you might be missing a semicolon here",
|
||||
";",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
Some(span.shrink_to_hi())
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
err.emit();
|
||||
_ => None,
|
||||
};
|
||||
|
||||
parser.sess.emit_err(IncompleteParse {
|
||||
span: def_site_span,
|
||||
token,
|
||||
label_span: span,
|
||||
macro_path,
|
||||
kind_name,
|
||||
add_semicolon,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1766,9 +1755,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
if self.expand_cfg_true(node, attr, pos) {
|
||||
continue;
|
||||
}
|
||||
let msg =
|
||||
format!("removing {} is not supported in this position", Node::descr());
|
||||
self.cx.span_err(span, &msg);
|
||||
|
||||
self.cx.emit_err(RemoveNodeNotSupported { span, descr: Node::descr() });
|
||||
continue;
|
||||
}
|
||||
sym::cfg_attr => {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#![feature(rustc_attrs)]
|
||||
#![feature(try_blocks)]
|
||||
#![recursion_limit = "256"]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate rustc_macros;
|
||||
|
|
@ -31,8 +32,13 @@ pub mod config;
|
|||
pub mod errors;
|
||||
pub mod expand;
|
||||
pub mod module;
|
||||
|
||||
// FIXME(Nilstrieb) Translate proc_macro diagnostics
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
pub mod proc_macro;
|
||||
|
||||
// FIXME(Nilstrieb) Translate macro_rules diagnostics
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
pub(crate) mod mbe;
|
||||
|
||||
// HACK(Centril, #64197): These shouldn't really be here.
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
use crate::base::ModuleData;
|
||||
use crate::errors::{
|
||||
ModuleCircular, ModuleFileNotFound, ModuleInBlock, ModuleInBlockName, ModuleMultipleCandidates,
|
||||
};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{token, AttrVec, Attribute, Inline, Item, ModSpans};
|
||||
use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_parse::new_parser_from_file;
|
||||
use rustc_parse::validate_attr;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::{sym, Ident};
|
||||
use rustc_span::Span;
|
||||
use std::iter::once;
|
||||
|
||||
use std::path::{self, Path, PathBuf};
|
||||
|
||||
|
|
@ -242,57 +246,41 @@ pub fn default_submod_path<'a>(
|
|||
|
||||
impl ModError<'_> {
|
||||
fn report(self, sess: &Session, span: Span) -> ErrorGuaranteed {
|
||||
let diag = &sess.parse_sess.span_diagnostic;
|
||||
match self {
|
||||
ModError::CircularInclusion(file_paths) => {
|
||||
let mut msg = String::from("circular modules: ");
|
||||
for file_path in &file_paths {
|
||||
msg.push_str(&file_path.display().to_string());
|
||||
msg.push_str(" -> ");
|
||||
}
|
||||
msg.push_str(&file_paths[0].display().to_string());
|
||||
diag.struct_span_err(span, &msg)
|
||||
let path_to_string = |path: &PathBuf| path.display().to_string();
|
||||
|
||||
let paths = file_paths
|
||||
.iter()
|
||||
.map(path_to_string)
|
||||
.chain(once(path_to_string(&file_paths[0])))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let modules = paths.join(" -> ");
|
||||
|
||||
sess.emit_err(ModuleCircular { span, modules })
|
||||
}
|
||||
ModError::ModInBlock(ident) => {
|
||||
let msg = "cannot declare a non-inline module inside a block unless it has a path attribute";
|
||||
let mut err = diag.struct_span_err(span, msg);
|
||||
if let Some(ident) = ident {
|
||||
let note =
|
||||
format!("maybe `use` the module `{}` instead of redeclaring it", ident);
|
||||
err.span_note(span, ¬e);
|
||||
}
|
||||
err
|
||||
}
|
||||
ModError::FileNotFound(ident, default_path, secondary_path) => {
|
||||
let mut err = struct_span_err!(
|
||||
diag,
|
||||
ModError::ModInBlock(ident) => sess.emit_err(ModuleInBlock {
|
||||
span,
|
||||
name: ident.map(|name| ModuleInBlockName { span, name }),
|
||||
}),
|
||||
ModError::FileNotFound(name, default_path, secondary_path) => {
|
||||
sess.emit_err(ModuleFileNotFound {
|
||||
span,
|
||||
E0583,
|
||||
"file not found for module `{}`",
|
||||
ident,
|
||||
);
|
||||
err.help(&format!(
|
||||
"to create the module `{}`, create file \"{}\" or \"{}\"",
|
||||
ident,
|
||||
default_path.display(),
|
||||
secondary_path.display(),
|
||||
));
|
||||
err
|
||||
name,
|
||||
default_path: default_path.display().to_string(),
|
||||
secondary_path: secondary_path.display().to_string(),
|
||||
})
|
||||
}
|
||||
ModError::MultipleCandidates(ident, default_path, secondary_path) => {
|
||||
let mut err = struct_span_err!(
|
||||
diag,
|
||||
ModError::MultipleCandidates(name, default_path, secondary_path) => {
|
||||
sess.emit_err(ModuleMultipleCandidates {
|
||||
span,
|
||||
E0761,
|
||||
"file for module `{}` found at both \"{}\" and \"{}\"",
|
||||
ident,
|
||||
default_path.display(),
|
||||
secondary_path.display(),
|
||||
);
|
||||
err.help("delete or rename one of them to remove the ambiguity");
|
||||
err
|
||||
name,
|
||||
default_path: default_path.display().to_string(),
|
||||
secondary_path: secondary_path.display().to_string(),
|
||||
})
|
||||
}
|
||||
ModError::ParserError(err) => err,
|
||||
}.emit()
|
||||
ModError::ParserError(mut err) => err.emit(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
|
|||
false,
|
||||
);
|
||||
let handler = Handler::with_emitter(true, None, Box::new(emitter));
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
handler.span_err(msp, "foo");
|
||||
|
||||
assert!(
|
||||
|
|
|
|||
|
|
@ -394,7 +394,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
|
||||
gated!(
|
||||
no_sanitize, Normal,
|
||||
template!(List: "address, memory, thread"), DuplicatesOk,
|
||||
template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
|
||||
experimental!(no_sanitize)
|
||||
),
|
||||
gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
|
||||
|
|
|
|||
|
|
@ -955,7 +955,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
|
|||
.struct_span_err(
|
||||
attr.span,
|
||||
"the `#[rustc_must_implement_one_of]` attribute must be \
|
||||
used with at least 2 args",
|
||||
used with at least 2 args",
|
||||
)
|
||||
.emit();
|
||||
|
||||
|
|
@ -987,7 +987,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
|
|||
tcx.sess
|
||||
.struct_span_err(
|
||||
item.span,
|
||||
"This function doesn't have a default implementation",
|
||||
"function doesn't have a default implementation",
|
||||
)
|
||||
.span_note(attr_span, "required by this annotation")
|
||||
.emit();
|
||||
|
|
@ -999,17 +999,17 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
|
|||
}
|
||||
Some(item) => {
|
||||
tcx.sess
|
||||
.struct_span_err(item.span, "Not a function")
|
||||
.struct_span_err(item.span, "not a function")
|
||||
.span_note(attr_span, "required by this annotation")
|
||||
.note(
|
||||
"All `#[rustc_must_implement_one_of]` arguments \
|
||||
must be associated function names",
|
||||
"all `#[rustc_must_implement_one_of]` arguments must be associated \
|
||||
function names",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
None => {
|
||||
tcx.sess
|
||||
.struct_span_err(ident.span, "Function not found in this trait")
|
||||
.struct_span_err(ident.span, "function not found in this trait")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
|
@ -1027,11 +1027,8 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
|
|||
for ident in &*list {
|
||||
if let Some(dup) = set.insert(ident.name, ident.span) {
|
||||
tcx.sess
|
||||
.struct_span_err(vec![dup, ident.span], "Functions names are duplicated")
|
||||
.note(
|
||||
"All `#[rustc_must_implement_one_of]` arguments \
|
||||
must be unique",
|
||||
)
|
||||
.struct_span_err(vec![dup, ident.span], "functions names are duplicated")
|
||||
.note("all `#[rustc_must_implement_one_of]` arguments must be unique")
|
||||
.emit();
|
||||
|
||||
no_dups = false;
|
||||
|
|
@ -1849,6 +1846,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
|
|||
codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS;
|
||||
} else if item.has_name(sym::cfi) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
|
||||
} else if item.has_name(sym::kcfi) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI;
|
||||
} else if item.has_name(sym::memory) {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
|
||||
} else if item.has_name(sym::memtag) {
|
||||
|
|
@ -1862,7 +1861,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
|
|||
} else {
|
||||
tcx.sess
|
||||
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
|
||||
.note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
|
||||
.note("expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
|
|||
let generics = tcx.generics_of(parent_def_id.to_def_id());
|
||||
let param_def_idx = generics.param_def_id_to_index[¶m_id.to_def_id()];
|
||||
// In the above example this would be .params[..N#0]
|
||||
let params = generics.params[..param_def_idx as usize].to_owned();
|
||||
let params = generics.params_to(param_def_idx as usize, tcx).to_owned();
|
||||
let param_def_id_to_index =
|
||||
params.iter().map(|param| (param.def_id, param.index)).collect();
|
||||
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
error: Option<TypeError<'_>>,
|
||||
error: Option<TypeError<'tcx>>,
|
||||
) {
|
||||
let parent = self.tcx.hir().get_parent_node(expr.hir_id);
|
||||
match (self.tcx.hir().find(parent), error) {
|
||||
|
|
@ -286,6 +286,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
err.downgrade_to_delayed_bug();
|
||||
}
|
||||
}
|
||||
(
|
||||
Some(hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Binary(_, lhs, rhs), ..
|
||||
})),
|
||||
Some(TypeError::Sorts(ExpectedFound { expected, .. })),
|
||||
) if rhs.hir_id == expr.hir_id
|
||||
&& self.typeck_results.borrow().expr_ty_adjusted_opt(lhs) == Some(expected) =>
|
||||
{
|
||||
err.span_label(lhs.span, &format!("expected because this is `{expected}`"));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,22 +56,17 @@ use crate::infer::ExpectedFound;
|
|||
use crate::traits::error_reporting::report_object_safety_error;
|
||||
use crate::traits::{
|
||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||
StatementAsExpression,
|
||||
};
|
||||
|
||||
use crate::errors::SuggAddLetForLetChains;
|
||||
use hir::intravisit::{walk_expr, walk_stmt};
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, DefKind};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::Node;
|
||||
use rustc_middle::dep_graph::DepContext;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::{
|
||||
self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
|
||||
|
|
@ -84,6 +79,7 @@ use std::path::PathBuf;
|
|||
use std::{cmp, fmt, iter};
|
||||
|
||||
mod note;
|
||||
mod suggest;
|
||||
|
||||
pub(crate) mod need_type_info;
|
||||
pub use need_type_info::TypeAnnotationNeeded;
|
||||
|
|
@ -807,87 +803,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_remove_semi_or_return_binding(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
first_id: Option<hir::HirId>,
|
||||
first_ty: Ty<'tcx>,
|
||||
first_span: Span,
|
||||
second_id: Option<hir::HirId>,
|
||||
second_ty: Ty<'tcx>,
|
||||
second_span: Span,
|
||||
) {
|
||||
let remove_semicolon = [
|
||||
(first_id, self.resolve_vars_if_possible(second_ty)),
|
||||
(second_id, self.resolve_vars_if_possible(first_ty)),
|
||||
]
|
||||
.into_iter()
|
||||
.find_map(|(id, ty)| {
|
||||
let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None };
|
||||
self.could_remove_semicolon(blk, ty)
|
||||
});
|
||||
match remove_semicolon {
|
||||
Some((sp, StatementAsExpression::NeedsBoxing)) => {
|
||||
err.multipart_suggestion(
|
||||
"consider removing this semicolon and boxing the expressions",
|
||||
vec![
|
||||
(first_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(first_span.shrink_to_hi(), ")".to_string()),
|
||||
(second_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(second_span.shrink_to_hi(), ")".to_string()),
|
||||
(sp, String::new()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
Some((sp, StatementAsExpression::CorrectType)) => {
|
||||
err.span_suggestion_short(
|
||||
sp,
|
||||
"consider removing this semicolon",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
|
||||
if let Some(id) = id
|
||||
&& let hir::Node::Block(blk) = self.tcx.hir().get(id)
|
||||
&& self.consider_returning_binding(blk, ty, err)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_boxing_for_return_impl_trait(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
return_sp: Span,
|
||||
arm_spans: impl Iterator<Item = Span>,
|
||||
) {
|
||||
err.multipart_suggestion(
|
||||
"you could change the return type to be a boxed trait object",
|
||||
vec![
|
||||
(return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
|
||||
(return_sp.shrink_to_hi(), ">".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
let sugg = arm_spans
|
||||
.flat_map(|sp| {
|
||||
[(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
|
||||
.into_iter()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
err.multipart_suggestion(
|
||||
"if you change the return type to expect trait objects, box the returned expressions",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
||||
/// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
|
||||
/// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
|
||||
/// populate `other_value` with `other_ty`.
|
||||
|
|
@ -1944,310 +1859,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
debug!(?diag);
|
||||
}
|
||||
|
||||
fn suggest_tuple_pattern(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
// Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
|
||||
// some modifications due to that being in typeck and this being in infer.
|
||||
if let ObligationCauseCode::Pattern { .. } = cause.code() {
|
||||
if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
|
||||
let compatible_variants: Vec<_> = expected_adt
|
||||
.variants()
|
||||
.iter()
|
||||
.filter(|variant| {
|
||||
variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
|
||||
})
|
||||
.filter_map(|variant| {
|
||||
let sole_field = &variant.fields[0];
|
||||
let sole_field_ty = sole_field.ty(self.tcx, substs);
|
||||
if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
|
||||
let variant_path =
|
||||
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
|
||||
// FIXME #56861: DRYer prelude filtering
|
||||
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
|
||||
if let Some((_, path)) = path.split_once("::") {
|
||||
return Some(path.to_string());
|
||||
}
|
||||
}
|
||||
Some(variant_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
match &compatible_variants[..] {
|
||||
[] => {}
|
||||
[variant] => {
|
||||
diag.multipart_suggestion_verbose(
|
||||
&format!("try wrapping the pattern in `{}`", variant),
|
||||
vec![
|
||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
// More than one matching variant.
|
||||
diag.multipart_suggestions(
|
||||
&format!(
|
||||
"try wrapping the pattern in a variant of `{}`",
|
||||
self.tcx.def_path_str(expected_adt.did())
|
||||
),
|
||||
compatible_variants.into_iter().map(|variant| {
|
||||
vec![
|
||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
||||
]
|
||||
}),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A possible error is to forget to add `.await` when using futures:
|
||||
///
|
||||
/// ```compile_fail,E0308
|
||||
/// async fn make_u32() -> u32 {
|
||||
/// 22
|
||||
/// }
|
||||
///
|
||||
/// fn take_u32(x: u32) {}
|
||||
///
|
||||
/// async fn foo() {
|
||||
/// let x = make_u32();
|
||||
/// take_u32(x);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
|
||||
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
|
||||
/// `.await` to the tail of the expression.
|
||||
fn suggest_await_on_expect_found(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_span: Span,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
debug!(
|
||||
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
|
||||
exp_span, exp_found.expected, exp_found.found,
|
||||
);
|
||||
|
||||
if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() {
|
||||
return;
|
||||
}
|
||||
|
||||
match (
|
||||
self.get_impl_future_output_ty(exp_found.expected),
|
||||
self.get_impl_future_output_ty(exp_found.found),
|
||||
) {
|
||||
(Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
|
||||
.code()
|
||||
{
|
||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||
diag.multipart_suggestion(
|
||||
"consider `await`ing on both `Future`s",
|
||||
vec![
|
||||
(then_span.shrink_to_hi(), ".await".to_string()),
|
||||
(exp_span.shrink_to_hi(), ".await".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
prior_arms,
|
||||
..
|
||||
}) => {
|
||||
if let [.., arm_span] = &prior_arms[..] {
|
||||
diag.multipart_suggestion(
|
||||
"consider `await`ing on both `Future`s",
|
||||
vec![
|
||||
(arm_span.shrink_to_hi(), ".await".to_string()),
|
||||
(exp_span.shrink_to_hi(), ".await".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
diag.help("consider `await`ing on both `Future`s");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
diag.help("consider `await`ing on both `Future`s");
|
||||
}
|
||||
},
|
||||
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
|
||||
diag.span_suggestion_verbose(
|
||||
exp_span.shrink_to_hi(),
|
||||
"consider `await`ing on the `Future`",
|
||||
".await",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
|
||||
{
|
||||
ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
|
||||
diag.span_suggestion_verbose(
|
||||
then_span.shrink_to_hi(),
|
||||
"consider `await`ing on the `Future`",
|
||||
".await",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||
diag.span_suggestion_verbose(
|
||||
then_span.shrink_to_hi(),
|
||||
"consider `await`ing on the `Future`",
|
||||
".await",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
ref prior_arms,
|
||||
..
|
||||
}) => {
|
||||
diag.multipart_suggestion_verbose(
|
||||
"consider `await`ing on the `Future`",
|
||||
prior_arms
|
||||
.iter()
|
||||
.map(|arm| (arm.shrink_to_hi(), ".await".to_string()))
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_accessing_field_where_appropriate(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
debug!(
|
||||
"suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
|
||||
cause, exp_found
|
||||
);
|
||||
if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
|
||||
if expected_def.is_enum() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((name, ty)) = expected_def
|
||||
.non_enum_variant()
|
||||
.fields
|
||||
.iter()
|
||||
.filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
|
||||
.map(|field| (field.name, field.ty(self.tcx, expected_substs)))
|
||||
.find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
|
||||
{
|
||||
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
let suggestion = if expected_def.is_struct() {
|
||||
format!("{}.{}", snippet, name)
|
||||
} else if expected_def.is_union() {
|
||||
format!("unsafe {{ {}.{} }}", snippet, name)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
&format!(
|
||||
"you might have meant to use field `{}` whose type is `{}`",
|
||||
name, ty
|
||||
),
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
|
||||
/// suggests it.
|
||||
fn suggest_as_ref_where_appropriate(
|
||||
&self,
|
||||
span: Span,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
||||
&& let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
|
||||
{
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
msg,
|
||||
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
|
||||
format!("{}.as_ref()", snippet.trim_start_matches('&')),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
|
||||
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
|
||||
(expected.kind(), found.kind())
|
||||
{
|
||||
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
|
||||
if exp_def == &found_def {
|
||||
let have_as_ref = &[
|
||||
(
|
||||
sym::Option,
|
||||
"you can convert from `&Option<T>` to `Option<&T>` using \
|
||||
`.as_ref()`",
|
||||
),
|
||||
(
|
||||
sym::Result,
|
||||
"you can convert from `&Result<T, E>` to \
|
||||
`Result<&T, &E>` using `.as_ref()`",
|
||||
),
|
||||
];
|
||||
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
|
||||
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
|
||||
}) {
|
||||
let mut show_suggestion = true;
|
||||
for (exp_ty, found_ty) in
|
||||
iter::zip(exp_substs.types(), found_substs.types())
|
||||
{
|
||||
match *exp_ty.kind() {
|
||||
ty::Ref(_, exp_ty, _) => {
|
||||
match (exp_ty.kind(), found_ty.kind()) {
|
||||
(_, ty::Param(_))
|
||||
| (_, ty::Infer(_))
|
||||
| (ty::Param(_), _)
|
||||
| (ty::Infer(_), _) => {}
|
||||
_ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
|
||||
_ => show_suggestion = false,
|
||||
};
|
||||
}
|
||||
ty::Param(_) | ty::Infer(_) => {}
|
||||
_ => show_suggestion = false,
|
||||
}
|
||||
}
|
||||
if show_suggestion {
|
||||
return Some(*msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn report_and_explain_type_error(
|
||||
&self,
|
||||
trace: TypeTrace<'tcx>,
|
||||
|
|
@ -2361,67 +1972,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
diag
|
||||
}
|
||||
|
||||
/// Try to find code with pattern `if Some(..) = expr`
|
||||
/// use a `visitor` to mark the `if` which its span contains given error span,
|
||||
/// and then try to find a assignment in the `cond` part, which span is equal with error span
|
||||
fn suggest_let_for_letchains(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
cause: &ObligationCause<'_>,
|
||||
span: Span,
|
||||
) {
|
||||
let hir = self.tcx.hir();
|
||||
let fn_hir_id = hir.get_parent_node(cause.body_id);
|
||||
if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
|
||||
let hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Fn(_sig, _, body_id), ..
|
||||
}) = node {
|
||||
let body = hir.body(*body_id);
|
||||
|
||||
/// Find the if expression with given span
|
||||
struct IfVisitor {
|
||||
pub result: bool,
|
||||
pub found_if: bool,
|
||||
pub err_span: Span,
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for IfVisitor {
|
||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
||||
if self.result { return; }
|
||||
match ex.kind {
|
||||
hir::ExprKind::If(cond, _, _) => {
|
||||
self.found_if = true;
|
||||
walk_expr(self, cond);
|
||||
self.found_if = false;
|
||||
}
|
||||
_ => walk_expr(self, ex),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
||||
if let hir::StmtKind::Local(hir::Local {
|
||||
span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
|
||||
}) = &ex.kind
|
||||
&& self.found_if
|
||||
&& span.eq(&self.err_span) {
|
||||
self.result = true;
|
||||
}
|
||||
walk_stmt(self, ex);
|
||||
}
|
||||
|
||||
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
|
||||
hir::intravisit::walk_body(self, body);
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
|
||||
visitor.visit_body(&body);
|
||||
if visitor.result {
|
||||
err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_tuple_wrap_err(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
|
|
@ -3253,211 +2803,3 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
/// Be helpful when the user wrote `{... expr; }` and taking the `;` off
|
||||
/// is enough to fix the error.
|
||||
pub fn could_remove_semicolon(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) -> Option<(Span, StatementAsExpression)> {
|
||||
let blk = blk.innermost_block();
|
||||
// Do not suggest if we have a tail expr.
|
||||
if blk.expr.is_some() {
|
||||
return None;
|
||||
}
|
||||
let last_stmt = blk.stmts.last()?;
|
||||
let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
|
||||
return None;
|
||||
};
|
||||
let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?;
|
||||
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
|
||||
_ if last_expr_ty.references_error() => return None,
|
||||
_ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
|
||||
StatementAsExpression::CorrectType
|
||||
}
|
||||
(ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
|
||||
if last_def_id == exp_def_id =>
|
||||
{
|
||||
StatementAsExpression::CorrectType
|
||||
}
|
||||
(ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
|
||||
debug!(
|
||||
"both opaque, likely future {:?} {:?} {:?} {:?}",
|
||||
last_def_id, last_bounds, exp_def_id, exp_bounds
|
||||
);
|
||||
|
||||
let last_local_id = last_def_id.as_local()?;
|
||||
let exp_local_id = exp_def_id.as_local()?;
|
||||
|
||||
match (
|
||||
&self.tcx.hir().expect_item(last_local_id).kind,
|
||||
&self.tcx.hir().expect_item(exp_local_id).kind,
|
||||
) {
|
||||
(
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
|
||||
) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
|
||||
match (left, right) {
|
||||
(
|
||||
hir::GenericBound::Trait(tl, ml),
|
||||
hir::GenericBound::Trait(tr, mr),
|
||||
) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
|
||||
&& ml == mr =>
|
||||
{
|
||||
true
|
||||
}
|
||||
(
|
||||
hir::GenericBound::LangItemTrait(langl, _, _, argsl),
|
||||
hir::GenericBound::LangItemTrait(langr, _, _, argsr),
|
||||
) if langl == langr => {
|
||||
// FIXME: consider the bounds!
|
||||
debug!("{:?} {:?}", argsl, argsr);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}) =>
|
||||
{
|
||||
StatementAsExpression::NeedsBoxing
|
||||
}
|
||||
_ => StatementAsExpression::CorrectType,
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let span = if last_stmt.span.from_expansion() {
|
||||
let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
|
||||
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
|
||||
} else {
|
||||
last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
|
||||
};
|
||||
Some((span, needs_box))
|
||||
}
|
||||
|
||||
/// Suggest returning a local binding with a compatible type if the block
|
||||
/// has no return expression.
|
||||
pub fn consider_returning_binding(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
) -> bool {
|
||||
let blk = blk.innermost_block();
|
||||
// Do not suggest if we have a tail expr.
|
||||
if blk.expr.is_some() {
|
||||
return false;
|
||||
}
|
||||
let mut shadowed = FxIndexSet::default();
|
||||
let mut candidate_idents = vec![];
|
||||
let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
|
||||
if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
|
||||
&& let Some(pat_ty) = self
|
||||
.typeck_results
|
||||
.as_ref()
|
||||
.and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
|
||||
{
|
||||
let pat_ty = self.resolve_vars_if_possible(pat_ty);
|
||||
if self.same_type_modulo_infer(pat_ty, expected_ty)
|
||||
&& !(pat_ty, expected_ty).references_error()
|
||||
&& shadowed.insert(ident.name)
|
||||
{
|
||||
candidate_idents.push((*ident, pat_ty));
|
||||
}
|
||||
}
|
||||
true
|
||||
};
|
||||
|
||||
let hir = self.tcx.hir();
|
||||
for stmt in blk.stmts.iter().rev() {
|
||||
let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
|
||||
local.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
match hir.find(hir.get_parent_node(blk.hir_id)) {
|
||||
Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
|
||||
match hir.find(hir.get_parent_node(*hir_id)) {
|
||||
Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
|
||||
pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
Some(
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
|
||||
| hir::Node::ImplItem(hir::ImplItem {
|
||||
kind: hir::ImplItemKind::Fn(_, body),
|
||||
..
|
||||
})
|
||||
| hir::Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
|
||||
..
|
||||
})
|
||||
| hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
|
||||
..
|
||||
}),
|
||||
) => {
|
||||
for param in hir.body(*body).params {
|
||||
param.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
}
|
||||
Some(hir::Node::Expr(hir::Expr {
|
||||
kind:
|
||||
hir::ExprKind::If(
|
||||
hir::Expr { kind: hir::ExprKind::Let(let_), .. },
|
||||
then_block,
|
||||
_,
|
||||
),
|
||||
..
|
||||
})) if then_block.hir_id == *hir_id => {
|
||||
let_.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match &candidate_idents[..] {
|
||||
[(ident, _ty)] => {
|
||||
let sm = self.tcx.sess.source_map();
|
||||
if let Some(stmt) = blk.stmts.last() {
|
||||
let stmt_span = sm.stmt_span(stmt.span, blk.span);
|
||||
let sugg = if sm.is_multiline(blk.span)
|
||||
&& let Some(spacing) = sm.indentation_before(stmt_span)
|
||||
{
|
||||
format!("\n{spacing}{ident}")
|
||||
} else {
|
||||
format!(" {ident}")
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
stmt_span.shrink_to_hi(),
|
||||
format!("consider returning the local binding `{ident}`"),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
let sugg = if sm.is_multiline(blk.span)
|
||||
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
|
||||
{
|
||||
format!("\n{spacing} {ident}\n{spacing}")
|
||||
} else {
|
||||
format!(" {ident} ")
|
||||
};
|
||||
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
|
||||
err.span_suggestion_verbose(
|
||||
sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
|
||||
format!("consider returning the local binding `{ident}`"),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
values if (1..3).contains(&values.len()) => {
|
||||
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
|
||||
err.span_note(spans, "consider returning one of these bindings");
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
427
compiler/rustc_infer/src/infer/error_reporting/note_region.rs
Normal file
427
compiler/rustc_infer/src/infer/error_reporting/note_region.rs
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
use crate::errors::RegionOriginNote;
|
||||
use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
|
||||
use crate::infer::{self, SubregionOrigin};
|
||||
use rustc_errors::{
|
||||
fluent, struct_span_err, AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
|
||||
};
|
||||
use rustc_middle::traits::ObligationCauseCode;
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::{self, Region};
|
||||
|
||||
use super::ObligationCauseAsDiagArg;
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) {
|
||||
match *origin {
|
||||
infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
|
||||
span: trace.cause.span,
|
||||
requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
|
||||
expected_found: self.values_str(trace.values),
|
||||
}
|
||||
.add_to_diagnostic(err),
|
||||
infer::Reborrow(span) => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diagnostic(err)
|
||||
}
|
||||
infer::ReborrowUpvar(span, ref upvar_id) => {
|
||||
let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
|
||||
RegionOriginNote::WithName {
|
||||
span,
|
||||
msg: fluent::infer_reborrow,
|
||||
name: &var_name.to_string(),
|
||||
continues: false,
|
||||
}
|
||||
.add_to_diagnostic(err);
|
||||
}
|
||||
infer::RelateObjectBound(span) => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
|
||||
.add_to_diagnostic(err);
|
||||
}
|
||||
infer::DataBorrowed(ty, span) => {
|
||||
RegionOriginNote::WithName {
|
||||
span,
|
||||
msg: fluent::infer_data_borrowed,
|
||||
name: &self.ty_to_string(ty),
|
||||
continues: false,
|
||||
}
|
||||
.add_to_diagnostic(err);
|
||||
}
|
||||
infer::ReferenceOutlivesReferent(ty, span) => {
|
||||
RegionOriginNote::WithName {
|
||||
span,
|
||||
msg: fluent::infer_reference_outlives_referent,
|
||||
name: &self.ty_to_string(ty),
|
||||
continues: false,
|
||||
}
|
||||
.add_to_diagnostic(err);
|
||||
}
|
||||
infer::RelateParamBound(span, ty, opt_span) => {
|
||||
RegionOriginNote::WithName {
|
||||
span,
|
||||
msg: fluent::infer_relate_param_bound,
|
||||
name: &self.ty_to_string(ty),
|
||||
continues: opt_span.is_some(),
|
||||
}
|
||||
.add_to_diagnostic(err);
|
||||
if let Some(span) = opt_span {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
|
||||
.add_to_diagnostic(err);
|
||||
}
|
||||
}
|
||||
infer::RelateRegionParamBound(span) => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
|
||||
.add_to_diagnostic(err);
|
||||
}
|
||||
infer::CompareImplItemObligation { span, .. } => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
|
||||
.add_to_diagnostic(err);
|
||||
}
|
||||
infer::CheckAssociatedTypeBounds { ref parent, .. } => {
|
||||
self.note_region_origin(err, &parent);
|
||||
}
|
||||
infer::AscribeUserTypeProvePredicate(span) => {
|
||||
RegionOriginNote::Plain {
|
||||
span,
|
||||
msg: fluent::infer_ascribe_user_type_prove_predicate,
|
||||
}
|
||||
.add_to_diagnostic(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn report_concrete_failure(
|
||||
&self,
|
||||
origin: SubregionOrigin<'tcx>,
|
||||
sub: Region<'tcx>,
|
||||
sup: Region<'tcx>,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
match origin {
|
||||
infer::Subtype(box trace) => {
|
||||
let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
|
||||
let mut err = self.report_and_explain_type_error(trace, terr);
|
||||
match (*sub, *sup) {
|
||||
(ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
|
||||
(ty::RePlaceholder(_), _) => {
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"",
|
||||
sup,
|
||||
" doesn't meet the lifetime requirements",
|
||||
None,
|
||||
);
|
||||
}
|
||||
(_, ty::RePlaceholder(_)) => {
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"the required lifetime does not necessarily outlive ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
note_and_explain_region(self.tcx, &mut err, "", sup, "...", None);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"...does not necessarily outlive ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
err
|
||||
}
|
||||
infer::Reborrow(span) => {
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0312,
|
||||
"lifetime of reference outlives lifetime of borrowed content..."
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"...the reference is valid for ",
|
||||
sub,
|
||||
"...",
|
||||
None,
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"...but the borrowed content is only valid for ",
|
||||
sup,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
err
|
||||
}
|
||||
infer::ReborrowUpvar(span, ref upvar_id) => {
|
||||
let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0313,
|
||||
"lifetime of borrowed pointer outlives lifetime of captured variable `{}`...",
|
||||
var_name
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"...the borrowed pointer is valid for ",
|
||||
sub,
|
||||
"...",
|
||||
None,
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
&format!("...but `{}` is only valid for ", var_name),
|
||||
sup,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
err
|
||||
}
|
||||
infer::RelateObjectBound(span) => {
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0476,
|
||||
"lifetime of the source pointer does not outlive lifetime bound of the \
|
||||
object type"
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"object type is valid for ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"source pointer is only valid for ",
|
||||
sup,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
err
|
||||
}
|
||||
infer::RelateParamBound(span, ty, opt_span) => {
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0477,
|
||||
"the type `{}` does not fulfill the required lifetime",
|
||||
self.ty_to_string(ty)
|
||||
);
|
||||
match *sub {
|
||||
ty::ReStatic => note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"type must satisfy ",
|
||||
sub,
|
||||
if opt_span.is_some() { " as required by this binding" } else { "" },
|
||||
opt_span,
|
||||
),
|
||||
_ => note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"type must outlive ",
|
||||
sub,
|
||||
if opt_span.is_some() { " as required by this binding" } else { "" },
|
||||
opt_span,
|
||||
),
|
||||
}
|
||||
err
|
||||
}
|
||||
infer::RelateRegionParamBound(span) => {
|
||||
let mut err =
|
||||
struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"lifetime parameter instantiated with ",
|
||||
sup,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"but lifetime parameter must outlive ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
err
|
||||
}
|
||||
infer::DataBorrowed(ty, span) => {
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0490,
|
||||
"a value of type `{}` is borrowed for too long",
|
||||
self.ty_to_string(ty)
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"the type is valid for ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"but the borrow lasts for ",
|
||||
sup,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
err
|
||||
}
|
||||
infer::ReferenceOutlivesReferent(ty, span) => {
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0491,
|
||||
"in type `{}`, reference has a longer lifetime than the data it references",
|
||||
self.ty_to_string(ty)
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"the pointer is valid for ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"but the referenced data is only valid for ",
|
||||
sup,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
err
|
||||
}
|
||||
infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => self
|
||||
.report_extra_impl_obligation(
|
||||
span,
|
||||
impl_item_def_id,
|
||||
trait_item_def_id,
|
||||
&format!("`{}: {}`", sup, sub),
|
||||
),
|
||||
infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
|
||||
let mut err = self.report_concrete_failure(*parent, sub, sup);
|
||||
|
||||
let trait_item_span = self.tcx.def_span(trait_item_def_id);
|
||||
let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
|
||||
err.span_label(
|
||||
trait_item_span,
|
||||
format!("definition of `{}` from trait", item_name),
|
||||
);
|
||||
|
||||
let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id);
|
||||
let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id);
|
||||
|
||||
let impl_predicates: rustc_data_structures::fx::FxHashSet<_> =
|
||||
impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect();
|
||||
let clauses: Vec<_> = trait_predicates
|
||||
.predicates
|
||||
.into_iter()
|
||||
.filter(|&(pred, _)| !impl_predicates.contains(pred))
|
||||
.map(|(pred, _)| format!("{}", pred))
|
||||
.collect();
|
||||
|
||||
if !clauses.is_empty() {
|
||||
let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap();
|
||||
let where_clause_span = generics.tail_span_for_predicate_suggestion();
|
||||
|
||||
let suggestion = format!(
|
||||
"{} {}",
|
||||
generics.add_where_or_trailing_comma(),
|
||||
clauses.join(", "),
|
||||
);
|
||||
err.span_suggestion(
|
||||
where_clause_span,
|
||||
&format!(
|
||||
"try copying {} from the trait",
|
||||
if clauses.len() > 1 { "these clauses" } else { "this clause" }
|
||||
),
|
||||
suggestion,
|
||||
rustc_errors::Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
||||
err
|
||||
}
|
||||
infer::AscribeUserTypeProvePredicate(span) => {
|
||||
let mut err =
|
||||
struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"lifetime instantiated with ",
|
||||
sup,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
"but lifetime must outlive ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn report_placeholder_failure(
|
||||
&self,
|
||||
placeholder_origin: SubregionOrigin<'tcx>,
|
||||
sub: Region<'tcx>,
|
||||
sup: Region<'tcx>,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
// I can't think how to do better than this right now. -nikomatsakis
|
||||
debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
|
||||
match placeholder_origin {
|
||||
infer::Subtype(box ref trace)
|
||||
if matches!(
|
||||
&trace.cause.code().peel_derives(),
|
||||
ObligationCauseCode::BindingObligation(..)
|
||||
| ObligationCauseCode::ExprBindingObligation(..)
|
||||
) =>
|
||||
{
|
||||
// Hack to get around the borrow checker because trace.cause has an `Rc`.
|
||||
if let ObligationCauseCode::BindingObligation(_, span)
|
||||
| ObligationCauseCode::ExprBindingObligation(_, span, ..) =
|
||||
&trace.cause.code().peel_derives()
|
||||
{
|
||||
let span = *span;
|
||||
let mut err = self.report_concrete_failure(placeholder_origin, sub, sup);
|
||||
err.span_note(span, "the lifetime requirement is introduced here");
|
||||
err
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
infer::Subtype(box trace) => {
|
||||
let terr = TypeError::RegionsPlaceholderMismatch;
|
||||
return self.report_and_explain_type_error(trace, terr);
|
||||
}
|
||||
_ => return self.report_concrete_failure(placeholder_origin, sub, sup),
|
||||
}
|
||||
}
|
||||
}
|
||||
672
compiler/rustc_infer/src/infer/error_reporting/suggest.rs
Normal file
672
compiler/rustc_infer/src/infer/error_reporting/suggest.rs
Normal file
|
|
@ -0,0 +1,672 @@
|
|||
use hir::def::CtorKind;
|
||||
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::traits::{
|
||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||
StatementAsExpression,
|
||||
};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self as ty, Ty, TypeVisitable};
|
||||
use rustc_span::{sym, BytePos, Span};
|
||||
|
||||
use crate::errors::SuggAddLetForLetChains;
|
||||
|
||||
use super::TypeErrCtxt;
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
pub(super) fn suggest_remove_semi_or_return_binding(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
first_id: Option<hir::HirId>,
|
||||
first_ty: Ty<'tcx>,
|
||||
first_span: Span,
|
||||
second_id: Option<hir::HirId>,
|
||||
second_ty: Ty<'tcx>,
|
||||
second_span: Span,
|
||||
) {
|
||||
let remove_semicolon = [
|
||||
(first_id, self.resolve_vars_if_possible(second_ty)),
|
||||
(second_id, self.resolve_vars_if_possible(first_ty)),
|
||||
]
|
||||
.into_iter()
|
||||
.find_map(|(id, ty)| {
|
||||
let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None };
|
||||
self.could_remove_semicolon(blk, ty)
|
||||
});
|
||||
match remove_semicolon {
|
||||
Some((sp, StatementAsExpression::NeedsBoxing)) => {
|
||||
err.multipart_suggestion(
|
||||
"consider removing this semicolon and boxing the expressions",
|
||||
vec![
|
||||
(first_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(first_span.shrink_to_hi(), ")".to_string()),
|
||||
(second_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(second_span.shrink_to_hi(), ")".to_string()),
|
||||
(sp, String::new()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
Some((sp, StatementAsExpression::CorrectType)) => {
|
||||
err.span_suggestion_short(
|
||||
sp,
|
||||
"consider removing this semicolon",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
|
||||
if let Some(id) = id
|
||||
&& let hir::Node::Block(blk) = self.tcx.hir().get(id)
|
||||
&& self.consider_returning_binding(blk, ty, err)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_boxing_for_return_impl_trait(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
return_sp: Span,
|
||||
arm_spans: impl Iterator<Item = Span>,
|
||||
) {
|
||||
err.multipart_suggestion(
|
||||
"you could change the return type to be a boxed trait object",
|
||||
vec![
|
||||
(return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
|
||||
(return_sp.shrink_to_hi(), ">".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
let sugg = arm_spans
|
||||
.flat_map(|sp| {
|
||||
[(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
|
||||
.into_iter()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
err.multipart_suggestion(
|
||||
"if you change the return type to expect trait objects, box the returned expressions",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn suggest_tuple_pattern(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
// Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
|
||||
// some modifications due to that being in typeck and this being in infer.
|
||||
if let ObligationCauseCode::Pattern { .. } = cause.code() {
|
||||
if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
|
||||
let compatible_variants: Vec<_> = expected_adt
|
||||
.variants()
|
||||
.iter()
|
||||
.filter(|variant| {
|
||||
variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
|
||||
})
|
||||
.filter_map(|variant| {
|
||||
let sole_field = &variant.fields[0];
|
||||
let sole_field_ty = sole_field.ty(self.tcx, substs);
|
||||
if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
|
||||
let variant_path =
|
||||
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
|
||||
// FIXME #56861: DRYer prelude filtering
|
||||
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
|
||||
if let Some((_, path)) = path.split_once("::") {
|
||||
return Some(path.to_string());
|
||||
}
|
||||
}
|
||||
Some(variant_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
match &compatible_variants[..] {
|
||||
[] => {}
|
||||
[variant] => {
|
||||
diag.multipart_suggestion_verbose(
|
||||
&format!("try wrapping the pattern in `{}`", variant),
|
||||
vec![
|
||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
// More than one matching variant.
|
||||
diag.multipart_suggestions(
|
||||
&format!(
|
||||
"try wrapping the pattern in a variant of `{}`",
|
||||
self.tcx.def_path_str(expected_adt.did())
|
||||
),
|
||||
compatible_variants.into_iter().map(|variant| {
|
||||
vec![
|
||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
||||
]
|
||||
}),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A possible error is to forget to add `.await` when using futures:
|
||||
///
|
||||
/// ```compile_fail,E0308
|
||||
/// async fn make_u32() -> u32 {
|
||||
/// 22
|
||||
/// }
|
||||
///
|
||||
/// fn take_u32(x: u32) {}
|
||||
///
|
||||
/// async fn foo() {
|
||||
/// let x = make_u32();
|
||||
/// take_u32(x);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
|
||||
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
|
||||
/// `.await` to the tail of the expression.
|
||||
pub(super) fn suggest_await_on_expect_found(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_span: Span,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
debug!(
|
||||
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
|
||||
exp_span, exp_found.expected, exp_found.found,
|
||||
);
|
||||
|
||||
if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() {
|
||||
return;
|
||||
}
|
||||
|
||||
match (
|
||||
self.get_impl_future_output_ty(exp_found.expected),
|
||||
self.get_impl_future_output_ty(exp_found.found),
|
||||
) {
|
||||
(Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
|
||||
.code()
|
||||
{
|
||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||
diag.multipart_suggestion(
|
||||
"consider `await`ing on both `Future`s",
|
||||
vec![
|
||||
(then_span.shrink_to_hi(), ".await".to_string()),
|
||||
(exp_span.shrink_to_hi(), ".await".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
prior_arms,
|
||||
..
|
||||
}) => {
|
||||
if let [.., arm_span] = &prior_arms[..] {
|
||||
diag.multipart_suggestion(
|
||||
"consider `await`ing on both `Future`s",
|
||||
vec![
|
||||
(arm_span.shrink_to_hi(), ".await".to_string()),
|
||||
(exp_span.shrink_to_hi(), ".await".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
diag.help("consider `await`ing on both `Future`s");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
diag.help("consider `await`ing on both `Future`s");
|
||||
}
|
||||
},
|
||||
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
|
||||
diag.span_suggestion_verbose(
|
||||
exp_span.shrink_to_hi(),
|
||||
"consider `await`ing on the `Future`",
|
||||
".await",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
|
||||
{
|
||||
ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
|
||||
diag.span_suggestion_verbose(
|
||||
then_span.shrink_to_hi(),
|
||||
"consider `await`ing on the `Future`",
|
||||
".await",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||
diag.span_suggestion_verbose(
|
||||
then_span.shrink_to_hi(),
|
||||
"consider `await`ing on the `Future`",
|
||||
".await",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
ref prior_arms,
|
||||
..
|
||||
}) => {
|
||||
diag.multipart_suggestion_verbose(
|
||||
"consider `await`ing on the `Future`",
|
||||
prior_arms
|
||||
.iter()
|
||||
.map(|arm| (arm.shrink_to_hi(), ".await".to_string()))
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_accessing_field_where_appropriate(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
debug!(
|
||||
"suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
|
||||
cause, exp_found
|
||||
);
|
||||
if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
|
||||
if expected_def.is_enum() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((name, ty)) = expected_def
|
||||
.non_enum_variant()
|
||||
.fields
|
||||
.iter()
|
||||
.filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
|
||||
.map(|field| (field.name, field.ty(self.tcx, expected_substs)))
|
||||
.find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
|
||||
{
|
||||
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
let suggestion = if expected_def.is_struct() {
|
||||
format!("{}.{}", snippet, name)
|
||||
} else if expected_def.is_union() {
|
||||
format!("unsafe {{ {}.{} }}", snippet, name)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
&format!(
|
||||
"you might have meant to use field `{}` whose type is `{}`",
|
||||
name, ty
|
||||
),
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
|
||||
/// suggests it.
|
||||
pub(super) fn suggest_as_ref_where_appropriate(
|
||||
&self,
|
||||
span: Span,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
||||
&& let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
|
||||
{
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
msg,
|
||||
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
|
||||
format!("{}.as_ref()", snippet.trim_start_matches('&')),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
|
||||
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
|
||||
(expected.kind(), found.kind())
|
||||
{
|
||||
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
|
||||
if exp_def == &found_def {
|
||||
let have_as_ref = &[
|
||||
(
|
||||
sym::Option,
|
||||
"you can convert from `&Option<T>` to `Option<&T>` using \
|
||||
`.as_ref()`",
|
||||
),
|
||||
(
|
||||
sym::Result,
|
||||
"you can convert from `&Result<T, E>` to \
|
||||
`Result<&T, &E>` using `.as_ref()`",
|
||||
),
|
||||
];
|
||||
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
|
||||
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
|
||||
}) {
|
||||
let mut show_suggestion = true;
|
||||
for (exp_ty, found_ty) in
|
||||
std::iter::zip(exp_substs.types(), found_substs.types())
|
||||
{
|
||||
match *exp_ty.kind() {
|
||||
ty::Ref(_, exp_ty, _) => {
|
||||
match (exp_ty.kind(), found_ty.kind()) {
|
||||
(_, ty::Param(_))
|
||||
| (_, ty::Infer(_))
|
||||
| (ty::Param(_), _)
|
||||
| (ty::Infer(_), _) => {}
|
||||
_ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
|
||||
_ => show_suggestion = false,
|
||||
};
|
||||
}
|
||||
ty::Param(_) | ty::Infer(_) => {}
|
||||
_ => show_suggestion = false,
|
||||
}
|
||||
}
|
||||
if show_suggestion {
|
||||
return Some(*msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Try to find code with pattern `if Some(..) = expr`
|
||||
/// use a `visitor` to mark the `if` which its span contains given error span,
|
||||
/// and then try to find a assignment in the `cond` part, which span is equal with error span
|
||||
pub(super) fn suggest_let_for_letchains(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
cause: &ObligationCause<'_>,
|
||||
span: Span,
|
||||
) {
|
||||
let hir = self.tcx.hir();
|
||||
let fn_hir_id = hir.get_parent_node(cause.body_id);
|
||||
if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
|
||||
let hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Fn(_sig, _, body_id), ..
|
||||
}) = node {
|
||||
let body = hir.body(*body_id);
|
||||
|
||||
/// Find the if expression with given span
|
||||
struct IfVisitor {
|
||||
pub result: bool,
|
||||
pub found_if: bool,
|
||||
pub err_span: Span,
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for IfVisitor {
|
||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
||||
if self.result { return; }
|
||||
match ex.kind {
|
||||
hir::ExprKind::If(cond, _, _) => {
|
||||
self.found_if = true;
|
||||
walk_expr(self, cond);
|
||||
self.found_if = false;
|
||||
}
|
||||
_ => walk_expr(self, ex),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
||||
if let hir::StmtKind::Local(hir::Local {
|
||||
span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
|
||||
}) = &ex.kind
|
||||
&& self.found_if
|
||||
&& span.eq(&self.err_span) {
|
||||
self.result = true;
|
||||
}
|
||||
walk_stmt(self, ex);
|
||||
}
|
||||
|
||||
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
|
||||
hir::intravisit::walk_body(self, body);
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
|
||||
visitor.visit_body(&body);
|
||||
if visitor.result {
|
||||
err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
/// Be helpful when the user wrote `{... expr; }` and taking the `;` off
|
||||
/// is enough to fix the error.
|
||||
pub fn could_remove_semicolon(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) -> Option<(Span, StatementAsExpression)> {
|
||||
let blk = blk.innermost_block();
|
||||
// Do not suggest if we have a tail expr.
|
||||
if blk.expr.is_some() {
|
||||
return None;
|
||||
}
|
||||
let last_stmt = blk.stmts.last()?;
|
||||
let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
|
||||
return None;
|
||||
};
|
||||
let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?;
|
||||
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
|
||||
_ if last_expr_ty.references_error() => return None,
|
||||
_ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
|
||||
StatementAsExpression::CorrectType
|
||||
}
|
||||
(ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
|
||||
if last_def_id == exp_def_id =>
|
||||
{
|
||||
StatementAsExpression::CorrectType
|
||||
}
|
||||
(ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
|
||||
debug!(
|
||||
"both opaque, likely future {:?} {:?} {:?} {:?}",
|
||||
last_def_id, last_bounds, exp_def_id, exp_bounds
|
||||
);
|
||||
|
||||
let last_local_id = last_def_id.as_local()?;
|
||||
let exp_local_id = exp_def_id.as_local()?;
|
||||
|
||||
match (
|
||||
&self.tcx.hir().expect_item(last_local_id).kind,
|
||||
&self.tcx.hir().expect_item(exp_local_id).kind,
|
||||
) {
|
||||
(
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
|
||||
) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
|
||||
match (left, right) {
|
||||
(
|
||||
hir::GenericBound::Trait(tl, ml),
|
||||
hir::GenericBound::Trait(tr, mr),
|
||||
) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
|
||||
&& ml == mr =>
|
||||
{
|
||||
true
|
||||
}
|
||||
(
|
||||
hir::GenericBound::LangItemTrait(langl, _, _, argsl),
|
||||
hir::GenericBound::LangItemTrait(langr, _, _, argsr),
|
||||
) if langl == langr => {
|
||||
// FIXME: consider the bounds!
|
||||
debug!("{:?} {:?}", argsl, argsr);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}) =>
|
||||
{
|
||||
StatementAsExpression::NeedsBoxing
|
||||
}
|
||||
_ => StatementAsExpression::CorrectType,
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let span = if last_stmt.span.from_expansion() {
|
||||
let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
|
||||
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
|
||||
} else {
|
||||
last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
|
||||
};
|
||||
Some((span, needs_box))
|
||||
}
|
||||
|
||||
/// Suggest returning a local binding with a compatible type if the block
|
||||
/// has no return expression.
|
||||
pub fn consider_returning_binding(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
) -> bool {
|
||||
let blk = blk.innermost_block();
|
||||
// Do not suggest if we have a tail expr.
|
||||
if blk.expr.is_some() {
|
||||
return false;
|
||||
}
|
||||
let mut shadowed = FxIndexSet::default();
|
||||
let mut candidate_idents = vec![];
|
||||
let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
|
||||
if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
|
||||
&& let Some(pat_ty) = self
|
||||
.typeck_results
|
||||
.as_ref()
|
||||
.and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
|
||||
{
|
||||
let pat_ty = self.resolve_vars_if_possible(pat_ty);
|
||||
if self.same_type_modulo_infer(pat_ty, expected_ty)
|
||||
&& !(pat_ty, expected_ty).references_error()
|
||||
&& shadowed.insert(ident.name)
|
||||
{
|
||||
candidate_idents.push((*ident, pat_ty));
|
||||
}
|
||||
}
|
||||
true
|
||||
};
|
||||
|
||||
let hir = self.tcx.hir();
|
||||
for stmt in blk.stmts.iter().rev() {
|
||||
let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
|
||||
local.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
match hir.find(hir.get_parent_node(blk.hir_id)) {
|
||||
Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
|
||||
match hir.find(hir.get_parent_node(*hir_id)) {
|
||||
Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
|
||||
pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
Some(
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
|
||||
| hir::Node::ImplItem(hir::ImplItem {
|
||||
kind: hir::ImplItemKind::Fn(_, body),
|
||||
..
|
||||
})
|
||||
| hir::Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
|
||||
..
|
||||
})
|
||||
| hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
|
||||
..
|
||||
}),
|
||||
) => {
|
||||
for param in hir.body(*body).params {
|
||||
param.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
}
|
||||
Some(hir::Node::Expr(hir::Expr {
|
||||
kind:
|
||||
hir::ExprKind::If(
|
||||
hir::Expr { kind: hir::ExprKind::Let(let_), .. },
|
||||
then_block,
|
||||
_,
|
||||
),
|
||||
..
|
||||
})) if then_block.hir_id == *hir_id => {
|
||||
let_.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match &candidate_idents[..] {
|
||||
[(ident, _ty)] => {
|
||||
let sm = self.tcx.sess.source_map();
|
||||
if let Some(stmt) = blk.stmts.last() {
|
||||
let stmt_span = sm.stmt_span(stmt.span, blk.span);
|
||||
let sugg = if sm.is_multiline(blk.span)
|
||||
&& let Some(spacing) = sm.indentation_before(stmt_span)
|
||||
{
|
||||
format!("\n{spacing}{ident}")
|
||||
} else {
|
||||
format!(" {ident}")
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
stmt_span.shrink_to_hi(),
|
||||
format!("consider returning the local binding `{ident}`"),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
let sugg = if sm.is_multiline(blk.span)
|
||||
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
|
||||
{
|
||||
format!("\n{spacing} {ident}\n{spacing}")
|
||||
} else {
|
||||
format!(" {ident} ")
|
||||
};
|
||||
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
|
||||
err.span_suggestion_verbose(
|
||||
sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
|
||||
format!("consider returning the local binding `{ident}`"),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
values if (1..3).contains(&values.len()) => {
|
||||
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
|
||||
err.span_note(spans, "consider returning one of these bindings");
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
203
compiler/rustc_infer/src/infer/note.rs
Normal file
203
compiler/rustc_infer/src/infer/note.rs
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
fn note_error_origin(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
|
||||
terr: TypeError<'tcx>,
|
||||
) {
|
||||
match *cause.code() {
|
||||
ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => {
|
||||
let ty = self.resolve_vars_if_possible(root_ty);
|
||||
if !matches!(ty.kind(), ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_)))
|
||||
{
|
||||
// don't show type `_`
|
||||
if span.desugaring_kind() == Some(DesugaringKind::ForLoop)
|
||||
&& let ty::Adt(def, substs) = ty.kind()
|
||||
&& Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option)
|
||||
{
|
||||
err.span_label(span, format!("this is an iterator with items of type `{}`", substs.type_at(0)));
|
||||
} else {
|
||||
err.span_label(span, format!("this expression has type `{}`", ty));
|
||||
}
|
||||
}
|
||||
if let Some(ty::error::ExpectedFound { found, .. }) = exp_found
|
||||
&& ty.is_box() && ty.boxed_ty() == found
|
||||
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
||||
{
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"consider dereferencing the boxed value",
|
||||
format!("*{}", snippet),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => {
|
||||
err.span_label(span, "expected due to this");
|
||||
}
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
arm_block_id,
|
||||
arm_span,
|
||||
arm_ty,
|
||||
prior_arm_block_id,
|
||||
prior_arm_span,
|
||||
prior_arm_ty,
|
||||
source,
|
||||
ref prior_arms,
|
||||
scrut_hir_id,
|
||||
opt_suggest_box_span,
|
||||
scrut_span,
|
||||
..
|
||||
}) => match source {
|
||||
hir::MatchSource::TryDesugar => {
|
||||
if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
|
||||
let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
|
||||
let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {
|
||||
let arg_expr = args.first().expect("try desugaring call w/out arg");
|
||||
self.typeck_results.as_ref().and_then(|typeck_results| {
|
||||
typeck_results.expr_ty_opt(arg_expr)
|
||||
})
|
||||
} else {
|
||||
bug!("try desugaring w/out call expr as scrutinee");
|
||||
};
|
||||
|
||||
match scrut_ty {
|
||||
Some(ty) if expected == ty => {
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
err.span_suggestion(
|
||||
source_map.end_point(cause.span),
|
||||
"try removing this `?`",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// `prior_arm_ty` can be `!`, `expected` will have better info when present.
|
||||
let t = self.resolve_vars_if_possible(match exp_found {
|
||||
Some(ty::error::ExpectedFound { expected, .. }) => expected,
|
||||
_ => prior_arm_ty,
|
||||
});
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
let mut any_multiline_arm = source_map.is_multiline(arm_span);
|
||||
if prior_arms.len() <= 4 {
|
||||
for sp in prior_arms {
|
||||
any_multiline_arm |= source_map.is_multiline(*sp);
|
||||
err.span_label(*sp, format!("this is found to be of type `{}`", t));
|
||||
}
|
||||
} else if let Some(sp) = prior_arms.last() {
|
||||
any_multiline_arm |= source_map.is_multiline(*sp);
|
||||
err.span_label(
|
||||
*sp,
|
||||
format!("this and all prior arms are found to be of type `{}`", t),
|
||||
);
|
||||
}
|
||||
let outer_error_span = if any_multiline_arm {
|
||||
// Cover just `match` and the scrutinee expression, not
|
||||
// the entire match body, to reduce diagram noise.
|
||||
cause.span.shrink_to_lo().to(scrut_span)
|
||||
} else {
|
||||
cause.span
|
||||
};
|
||||
let msg = "`match` arms have incompatible types";
|
||||
err.span_label(outer_error_span, msg);
|
||||
self.suggest_remove_semi_or_return_binding(
|
||||
err,
|
||||
prior_arm_block_id,
|
||||
prior_arm_ty,
|
||||
prior_arm_span,
|
||||
arm_block_id,
|
||||
arm_ty,
|
||||
arm_span,
|
||||
);
|
||||
if let Some(ret_sp) = opt_suggest_box_span {
|
||||
// Get return type span and point to it.
|
||||
self.suggest_boxing_for_return_impl_trait(
|
||||
err,
|
||||
ret_sp,
|
||||
prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
ObligationCauseCode::IfExpression(box IfExpressionCause {
|
||||
then_id,
|
||||
else_id,
|
||||
then_ty,
|
||||
else_ty,
|
||||
outer_span,
|
||||
opt_suggest_box_span,
|
||||
}) => {
|
||||
let then_span = self.find_block_span_from_hir_id(then_id);
|
||||
let else_span = self.find_block_span_from_hir_id(else_id);
|
||||
err.span_label(then_span, "expected because of this");
|
||||
if let Some(sp) = outer_span {
|
||||
err.span_label(sp, "`if` and `else` have incompatible types");
|
||||
}
|
||||
self.suggest_remove_semi_or_return_binding(
|
||||
err,
|
||||
Some(then_id),
|
||||
then_ty,
|
||||
then_span,
|
||||
Some(else_id),
|
||||
else_ty,
|
||||
else_span,
|
||||
);
|
||||
if let Some(ret_sp) = opt_suggest_box_span {
|
||||
self.suggest_boxing_for_return_impl_trait(
|
||||
err,
|
||||
ret_sp,
|
||||
[then_span, else_span].into_iter(),
|
||||
);
|
||||
}
|
||||
}
|
||||
ObligationCauseCode::LetElse => {
|
||||
err.help("try adding a diverging expression, such as `return` or `panic!(..)`");
|
||||
err.help("...or use `match` instead of `let...else`");
|
||||
}
|
||||
_ => {
|
||||
if let ObligationCauseCode::BindingObligation(_, span)
|
||||
| ObligationCauseCode::ExprBindingObligation(_, span, ..)
|
||||
= cause.code().peel_derives()
|
||||
&& let TypeError::RegionsPlaceholderMismatch = terr
|
||||
{
|
||||
err.span_note( * span,
|
||||
"the lifetime requirement is introduced here");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
/// Given a [`hir::Block`], get the span of its last expression or
|
||||
/// statement, peeling off any inner blocks.
|
||||
pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span {
|
||||
let block = block.innermost_block();
|
||||
if let Some(expr) = &block.expr {
|
||||
expr.span
|
||||
} else if let Some(stmt) = block.stmts.last() {
|
||||
// possibly incorrect trailing `;` in the else arm
|
||||
stmt.span
|
||||
} else {
|
||||
// empty block; point at its entirety
|
||||
block.span
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a [`hir::HirId`] for a block, get the span of its last expression
|
||||
/// or statement, peeling off any inner blocks.
|
||||
pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span {
|
||||
match self.tcx.hir().get(hir_id) {
|
||||
hir::Node::Block(blk) => self.find_block_span(blk),
|
||||
// The parser was in a weird state if either of these happen, but
|
||||
// it's better not to panic.
|
||||
hir::Node::Expr(e) => e.span,
|
||||
_ => rustc_span::DUMMY_SP,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -805,9 +805,9 @@ pub fn create_global_ctxt<'tcx>(
|
|||
});
|
||||
|
||||
let ty::ResolverOutputs {
|
||||
definitions,
|
||||
global_ctxt: untracked_resolutions,
|
||||
ast_lowering: untracked_resolver_for_lowering,
|
||||
untracked,
|
||||
} = resolver_outputs;
|
||||
|
||||
let gcx = sess.time("setup_global_ctxt", || {
|
||||
|
|
@ -817,8 +817,8 @@ pub fn create_global_ctxt<'tcx>(
|
|||
lint_store,
|
||||
arena,
|
||||
hir_arena,
|
||||
definitions,
|
||||
untracked_resolutions,
|
||||
untracked,
|
||||
krate,
|
||||
dep_graph,
|
||||
queries.on_disk_cache.as_ref().map(OnDiskCache::as_dyn),
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ use rustc_span::source_map::Spanned;
|
|||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{BytePos, InnerSpan, Span};
|
||||
use rustc_target::abi::{Abi, VariantIdx};
|
||||
use rustc_trait_selection::traits::{self, misc::can_type_implement_copy};
|
||||
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
|
||||
use rustc_trait_selection::traits::{self, misc::can_type_implement_copy, EvaluationResult};
|
||||
|
||||
use crate::nonstandard_style::{method_context, MethodLateContext};
|
||||
|
||||
|
|
@ -96,6 +97,7 @@ fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr {
|
|||
}
|
||||
|
||||
impl EarlyLintPass for WhileTrue {
|
||||
#[inline]
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
if let ast::ExprKind::While(cond, _, label) = &e.kind
|
||||
&& let cond = pierce_parens(cond)
|
||||
|
|
@ -360,6 +362,7 @@ impl EarlyLintPass for UnsafeCode {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
if let ast::ExprKind::Block(ref blk, _) = e.kind {
|
||||
// Don't warn about generated blocks; that'll just pollute the output.
|
||||
|
|
@ -582,6 +585,7 @@ impl MissingDoc {
|
|||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
#[inline]
|
||||
fn enter_lint_attrs(&mut self, _cx: &LateContext<'_>, attrs: &[ast::Attribute]) {
|
||||
let doc_hidden = self.doc_hidden()
|
||||
|| attrs.iter().any(|attr| {
|
||||
|
|
@ -750,10 +754,39 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
|||
if def.has_dtor(cx.tcx) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the type contains a raw pointer, it may represent something like a handle,
|
||||
// and recommending Copy might be a bad idea.
|
||||
for field in def.all_fields() {
|
||||
let did = field.did;
|
||||
if cx.tcx.type_of(did).is_unsafe_ptr() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let param_env = ty::ParamEnv::empty();
|
||||
if ty.is_copy_modulo_regions(cx.tcx, param_env) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We shouldn't recommend implementing `Copy` on stateful things,
|
||||
// such as iterators.
|
||||
if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) {
|
||||
if cx.tcx.infer_ctxt().build().type_implements_trait(iter_trait, [ty], param_env)
|
||||
== EvaluationResult::EvaluatedToOk
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Default value of clippy::trivially_copy_pass_by_ref
|
||||
const MAX_SIZE: u64 = 256;
|
||||
|
||||
if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()) {
|
||||
if size > MAX_SIZE {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if can_type_implement_copy(
|
||||
cx.tcx,
|
||||
param_env,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ pub struct EarlyContextAndPasses<'a> {
|
|||
}
|
||||
|
||||
impl<'a> EarlyContextAndPasses<'a> {
|
||||
fn check_id(&mut self, id: ast::NodeId) {
|
||||
// This always-inlined function is for the hot call site.
|
||||
#[inline(always)]
|
||||
fn inlined_check_id(&mut self, id: ast::NodeId) {
|
||||
for early_lint in self.context.buffered.take(id) {
|
||||
let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint;
|
||||
self.context.lookup_with_diagnostics(
|
||||
|
|
@ -50,6 +52,11 @@ impl<'a> EarlyContextAndPasses<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// This non-inlined function is for the cold call sites.
|
||||
fn check_id(&mut self, id: ast::NodeId) {
|
||||
self.inlined_check_id(id)
|
||||
}
|
||||
|
||||
/// Merge the lints specified by any lint attributes into the
|
||||
/// current lint context, call the provided function, then reset the
|
||||
/// lints in effect to their previous state.
|
||||
|
|
@ -61,7 +68,7 @@ impl<'a> EarlyContextAndPasses<'a> {
|
|||
debug!(?id);
|
||||
let push = self.context.builder.push(attrs, is_crate_node, None);
|
||||
|
||||
self.check_id(id);
|
||||
self.inlined_check_id(id);
|
||||
debug!("early context: enter_attrs({:?})", attrs);
|
||||
run_early_passes!(self, enter_lint_attrs, attrs);
|
||||
f(self);
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ impl EarlyLintPass for HiddenUnicodeCodepoints {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
|
||||
// byte strings are already handled well enough by `EscapeError::NonAsciiCharInByteString`
|
||||
match &expr.kind {
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
|
|||
late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new());
|
||||
}
|
||||
|
||||
// See the comment on `BuiltinCombinedEarlyLintPass`, which is similar.
|
||||
early_lint_methods!(
|
||||
declare_combined_early_lint_pass,
|
||||
[
|
||||
|
|
@ -137,6 +138,9 @@ early_lint_methods!(
|
|||
]
|
||||
);
|
||||
|
||||
// Declare `BuiltinCombinedEarlyLintPass`, a lint pass that combines multiple
|
||||
// lint passes into a single pass for maximum speed. Each `check_foo` method
|
||||
// within this pass simply calls `check_foo` once per listed lint.
|
||||
early_lint_methods!(
|
||||
declare_combined_early_lint_pass,
|
||||
[
|
||||
|
|
@ -162,7 +166,9 @@ early_lint_methods!(
|
|||
]
|
||||
);
|
||||
|
||||
// FIXME: Make a separate lint type which do not require typeck tables
|
||||
// FIXME: Make a separate lint type which does not require typeck tables.
|
||||
|
||||
// See the comment on `BuiltinCombinedEarlyLintPass`, which is similar.
|
||||
late_lint_methods!(
|
||||
declare_combined_late_lint_pass,
|
||||
[
|
||||
|
|
@ -179,10 +185,10 @@ late_lint_methods!(
|
|||
// Keeps a global list of foreign declarations.
|
||||
ClashingExternDeclarations: ClashingExternDeclarations::new(),
|
||||
]
|
||||
],
|
||||
['tcx]
|
||||
]
|
||||
);
|
||||
|
||||
// See the comment on `BuiltinCombinedEarlyLintPass`, which is similar.
|
||||
late_lint_methods!(
|
||||
declare_combined_late_lint_pass,
|
||||
[
|
||||
|
|
@ -229,8 +235,7 @@ late_lint_methods!(
|
|||
NamedAsmLabels: NamedAsmLabels,
|
||||
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
|
||||
]
|
||||
],
|
||||
['tcx]
|
||||
]
|
||||
);
|
||||
|
||||
pub fn new_lint_store(internal_lints: bool) -> LintStore {
|
||||
|
|
|
|||
|
|
@ -9,49 +9,49 @@ use rustc_span::Span;
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! late_lint_methods {
|
||||
($macro:path, $args:tt, [$hir:tt]) => (
|
||||
$macro!($args, [$hir], [
|
||||
fn check_body(a: &$hir hir::Body<$hir>);
|
||||
fn check_body_post(a: &$hir hir::Body<$hir>);
|
||||
($macro:path, $args:tt) => (
|
||||
$macro!($args, [
|
||||
fn check_body(a: &'tcx hir::Body<'tcx>);
|
||||
fn check_body_post(a: &'tcx hir::Body<'tcx>);
|
||||
fn check_crate();
|
||||
fn check_crate_post();
|
||||
fn check_mod(a: &$hir hir::Mod<$hir>, b: hir::HirId);
|
||||
fn check_foreign_item(a: &$hir hir::ForeignItem<$hir>);
|
||||
fn check_item(a: &$hir hir::Item<$hir>);
|
||||
fn check_item_post(a: &$hir hir::Item<$hir>);
|
||||
fn check_local(a: &$hir hir::Local<$hir>);
|
||||
fn check_block(a: &$hir hir::Block<$hir>);
|
||||
fn check_block_post(a: &$hir hir::Block<$hir>);
|
||||
fn check_stmt(a: &$hir hir::Stmt<$hir>);
|
||||
fn check_arm(a: &$hir hir::Arm<$hir>);
|
||||
fn check_pat(a: &$hir hir::Pat<$hir>);
|
||||
fn check_expr(a: &$hir hir::Expr<$hir>);
|
||||
fn check_expr_post(a: &$hir hir::Expr<$hir>);
|
||||
fn check_ty(a: &$hir hir::Ty<$hir>);
|
||||
fn check_generic_param(a: &$hir hir::GenericParam<$hir>);
|
||||
fn check_generics(a: &$hir hir::Generics<$hir>);
|
||||
fn check_poly_trait_ref(a: &$hir hir::PolyTraitRef<$hir>);
|
||||
fn check_mod(a: &'tcx hir::Mod<'tcx>, b: hir::HirId);
|
||||
fn check_foreign_item(a: &'tcx hir::ForeignItem<'tcx>);
|
||||
fn check_item(a: &'tcx hir::Item<'tcx>);
|
||||
fn check_item_post(a: &'tcx hir::Item<'tcx>);
|
||||
fn check_local(a: &'tcx hir::Local<'tcx>);
|
||||
fn check_block(a: &'tcx hir::Block<'tcx>);
|
||||
fn check_block_post(a: &'tcx hir::Block<'tcx>);
|
||||
fn check_stmt(a: &'tcx hir::Stmt<'tcx>);
|
||||
fn check_arm(a: &'tcx hir::Arm<'tcx>);
|
||||
fn check_pat(a: &'tcx hir::Pat<'tcx>);
|
||||
fn check_expr(a: &'tcx hir::Expr<'tcx>);
|
||||
fn check_expr_post(a: &'tcx hir::Expr<'tcx>);
|
||||
fn check_ty(a: &'tcx hir::Ty<'tcx>);
|
||||
fn check_generic_param(a: &'tcx hir::GenericParam<'tcx>);
|
||||
fn check_generics(a: &'tcx hir::Generics<'tcx>);
|
||||
fn check_poly_trait_ref(a: &'tcx hir::PolyTraitRef<'tcx>);
|
||||
fn check_fn(
|
||||
a: rustc_hir::intravisit::FnKind<$hir>,
|
||||
b: &$hir hir::FnDecl<$hir>,
|
||||
c: &$hir hir::Body<$hir>,
|
||||
a: rustc_hir::intravisit::FnKind<'tcx>,
|
||||
b: &'tcx hir::FnDecl<'tcx>,
|
||||
c: &'tcx hir::Body<'tcx>,
|
||||
d: Span,
|
||||
e: hir::HirId);
|
||||
fn check_trait_item(a: &$hir hir::TraitItem<$hir>);
|
||||
fn check_impl_item(a: &$hir hir::ImplItem<$hir>);
|
||||
fn check_impl_item_post(a: &$hir hir::ImplItem<$hir>);
|
||||
fn check_struct_def(a: &$hir hir::VariantData<$hir>);
|
||||
fn check_field_def(a: &$hir hir::FieldDef<$hir>);
|
||||
fn check_variant(a: &$hir hir::Variant<$hir>);
|
||||
fn check_path(a: &hir::Path<$hir>, b: hir::HirId);
|
||||
fn check_attribute(a: &$hir ast::Attribute);
|
||||
fn check_trait_item(a: &'tcx hir::TraitItem<'tcx>);
|
||||
fn check_impl_item(a: &'tcx hir::ImplItem<'tcx>);
|
||||
fn check_impl_item_post(a: &'tcx hir::ImplItem<'tcx>);
|
||||
fn check_struct_def(a: &'tcx hir::VariantData<'tcx>);
|
||||
fn check_field_def(a: &'tcx hir::FieldDef<'tcx>);
|
||||
fn check_variant(a: &'tcx hir::Variant<'tcx>);
|
||||
fn check_path(a: &hir::Path<'tcx>, b: hir::HirId);
|
||||
fn check_attribute(a: &'tcx ast::Attribute);
|
||||
|
||||
/// Called when entering a syntax node that can have lint attributes such
|
||||
/// as `#[allow(...)]`. Called with *all* the attributes of that node.
|
||||
fn enter_lint_attrs(a: &$hir [ast::Attribute]);
|
||||
fn enter_lint_attrs(a: &'tcx [ast::Attribute]);
|
||||
|
||||
/// Counterpart to `enter_lint_attrs`.
|
||||
fn exit_lint_attrs(a: &$hir [ast::Attribute]);
|
||||
fn exit_lint_attrs(a: &'tcx [ast::Attribute]);
|
||||
]);
|
||||
)
|
||||
}
|
||||
|
|
@ -66,21 +66,23 @@ macro_rules! late_lint_methods {
|
|||
// contains a few lint-specific methods with no equivalent in `Visitor`.
|
||||
|
||||
macro_rules! declare_late_lint_pass {
|
||||
([], [$hir:tt], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
|
||||
pub trait LateLintPass<$hir>: LintPass {
|
||||
$(#[inline(always)] fn $name(&mut self, _: &LateContext<$hir>, $(_: $arg),*) {})*
|
||||
([], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
|
||||
pub trait LateLintPass<'tcx>: LintPass {
|
||||
$(#[inline(always)] fn $name(&mut self, _: &LateContext<'tcx>, $(_: $arg),*) {})*
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
late_lint_methods!(declare_late_lint_pass, [], ['tcx]);
|
||||
// Declare the `LateLintPass` trait, which contains empty default definitions
|
||||
// for all the `check_*` methods.
|
||||
late_lint_methods!(declare_late_lint_pass, []);
|
||||
|
||||
impl LateLintPass<'_> for HardwiredLints {}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! expand_combined_late_lint_pass_method {
|
||||
([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({
|
||||
$($self.$passes.$name $params;)*
|
||||
([$($pass:ident),*], $self: ident, $name: ident, $params:tt) => ({
|
||||
$($self.$pass.$name $params;)*
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -95,28 +97,28 @@ macro_rules! expand_combined_late_lint_pass_methods {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! declare_combined_late_lint_pass {
|
||||
([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], [$hir:tt], $methods:tt) => (
|
||||
([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => (
|
||||
#[allow(non_snake_case)]
|
||||
$v struct $name {
|
||||
$($passes: $passes,)*
|
||||
$($pass: $pass,)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
$v fn new() -> Self {
|
||||
Self {
|
||||
$($passes: $constructor,)*
|
||||
$($pass: $constructor,)*
|
||||
}
|
||||
}
|
||||
|
||||
$v fn get_lints() -> LintArray {
|
||||
let mut lints = Vec::new();
|
||||
$(lints.extend_from_slice(&$passes::get_lints());)*
|
||||
$(lints.extend_from_slice(&$pass::get_lints());)*
|
||||
lints
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for $name {
|
||||
expand_combined_late_lint_pass_methods!([$($passes),*], $methods);
|
||||
expand_combined_late_lint_pass_methods!([$($pass),*], $methods);
|
||||
}
|
||||
|
||||
#[allow(rustc::lint_pass_impl_without_macro)]
|
||||
|
|
@ -176,12 +178,14 @@ macro_rules! declare_early_lint_pass {
|
|||
)
|
||||
}
|
||||
|
||||
// Declare the `EarlyLintPass` trait, which contains empty default definitions
|
||||
// for all the `check_*` methods.
|
||||
early_lint_methods!(declare_early_lint_pass, []);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! expand_combined_early_lint_pass_method {
|
||||
([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({
|
||||
$($self.$passes.$name $params;)*
|
||||
([$($pass:ident),*], $self: ident, $name: ident, $params:tt) => ({
|
||||
$($self.$pass.$name $params;)*
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -196,28 +200,28 @@ macro_rules! expand_combined_early_lint_pass_methods {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! declare_combined_early_lint_pass {
|
||||
([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], $methods:tt) => (
|
||||
([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => (
|
||||
#[allow(non_snake_case)]
|
||||
$v struct $name {
|
||||
$($passes: $passes,)*
|
||||
$($pass: $pass,)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
$v fn new() -> Self {
|
||||
Self {
|
||||
$($passes: $constructor,)*
|
||||
$($pass: $constructor,)*
|
||||
}
|
||||
}
|
||||
|
||||
$v fn get_lints() -> LintArray {
|
||||
let mut lints = Vec::new();
|
||||
$(lints.extend_from_slice(&$passes::get_lints());)*
|
||||
$(lints.extend_from_slice(&$pass::get_lints());)*
|
||||
lints
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for $name {
|
||||
expand_combined_early_lint_pass_methods!([$($passes),*], $methods);
|
||||
expand_combined_early_lint_pass_methods!([$($pass),*], $methods);
|
||||
}
|
||||
|
||||
#[allow(rustc::lint_pass_impl_without_macro)]
|
||||
|
|
|
|||
|
|
@ -617,7 +617,10 @@ trait UnusedDelimLint {
|
|||
lhs_needs_parens
|
||||
|| (followed_by_block
|
||||
&& match &inner.kind {
|
||||
ExprKind::Ret(_) | ExprKind::Break(..) | ExprKind::Yield(..) => true,
|
||||
ExprKind::Ret(_)
|
||||
| ExprKind::Break(..)
|
||||
| ExprKind::Yield(..)
|
||||
| ExprKind::Yeet(..) => true,
|
||||
ExprKind::Range(_lhs, Some(rhs), _limits) => {
|
||||
matches!(rhs.kind, ExprKind::Block(..))
|
||||
}
|
||||
|
|
@ -946,6 +949,7 @@ impl UnusedParens {
|
|||
}
|
||||
|
||||
impl EarlyLintPass for UnusedParens {
|
||||
#[inline]
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
match e.kind {
|
||||
ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => {
|
||||
|
|
@ -1164,6 +1168,7 @@ impl EarlyLintPass for UnusedBraces {
|
|||
<Self as UnusedDelimLint>::check_stmt(self, cx, s)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
<Self as UnusedDelimLint>::check_expr(self, cx, e);
|
||||
|
||||
|
|
|
|||
|
|
@ -238,18 +238,20 @@ fn main() {
|
|||
|
||||
if !is_crossed {
|
||||
cmd.arg("--system-libs");
|
||||
} else if target.contains("windows-gnu") {
|
||||
println!("cargo:rustc-link-lib=shell32");
|
||||
println!("cargo:rustc-link-lib=uuid");
|
||||
} else if target.contains("netbsd") || target.contains("haiku") || target.contains("darwin") {
|
||||
println!("cargo:rustc-link-lib=z");
|
||||
} else if target.starts_with("arm")
|
||||
}
|
||||
|
||||
if (target.starts_with("arm") && !target.contains("freebsd"))
|
||||
|| target.starts_with("mips-")
|
||||
|| target.starts_with("mipsel-")
|
||||
|| target.starts_with("powerpc-")
|
||||
{
|
||||
// 32-bit targets need to link libatomic.
|
||||
println!("cargo:rustc-link-lib=atomic");
|
||||
} else if target.contains("windows-gnu") {
|
||||
println!("cargo:rustc-link-lib=shell32");
|
||||
println!("cargo:rustc-link-lib=uuid");
|
||||
} else if target.contains("netbsd") || target.contains("haiku") || target.contains("darwin") {
|
||||
println!("cargo:rustc-link-lib=z");
|
||||
}
|
||||
cmd.args(&components);
|
||||
|
||||
|
|
|
|||
|
|
@ -1476,13 +1476,13 @@ extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) {
|
|||
|
||||
extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||
LLVMValueRef *Args, unsigned NumArgs,
|
||||
OperandBundleDef *Bundle) {
|
||||
OperandBundleDef **OpBundles,
|
||||
unsigned NumOpBundles) {
|
||||
Value *Callee = unwrap(Fn);
|
||||
FunctionType *FTy = unwrap<FunctionType>(Ty);
|
||||
unsigned Len = Bundle ? 1 : 0;
|
||||
ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
|
||||
return wrap(unwrap(B)->CreateCall(
|
||||
FTy, Callee, makeArrayRef(unwrap(Args), NumArgs), Bundles));
|
||||
FTy, Callee, makeArrayRef(unwrap(Args), NumArgs),
|
||||
makeArrayRef(*OpBundles, NumOpBundles)));
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) {
|
||||
|
|
@ -1522,14 +1522,14 @@ extern "C" LLVMValueRef
|
|||
LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||
LLVMValueRef *Args, unsigned NumArgs,
|
||||
LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
|
||||
OperandBundleDef *Bundle, const char *Name) {
|
||||
OperandBundleDef **OpBundles, unsigned NumOpBundles,
|
||||
const char *Name) {
|
||||
Value *Callee = unwrap(Fn);
|
||||
FunctionType *FTy = unwrap<FunctionType>(Ty);
|
||||
unsigned Len = Bundle ? 1 : 0;
|
||||
ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
|
||||
return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch),
|
||||
makeArrayRef(unwrap(Args), NumArgs),
|
||||
Bundles, Name));
|
||||
makeArrayRef(*OpBundles, NumOpBundles),
|
||||
Name));
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use rustc_ast::expand::allocator::AllocatorKind;
|
|||
use rustc_ast::{self as ast, *};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::sync::{Lrc, ReadGuard};
|
||||
use rustc_expand::base::SyntaxExtension;
|
||||
use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::Definitions;
|
||||
|
|
@ -68,11 +68,12 @@ impl std::fmt::Debug for CStore {
|
|||
pub struct CrateLoader<'a> {
|
||||
// Immutable configuration.
|
||||
sess: &'a Session,
|
||||
metadata_loader: Box<MetadataLoaderDyn>,
|
||||
metadata_loader: &'a MetadataLoaderDyn,
|
||||
definitions: ReadGuard<'a, Definitions>,
|
||||
local_crate_name: Symbol,
|
||||
// Mutable output.
|
||||
cstore: CStore,
|
||||
used_extern_options: FxHashSet<Symbol>,
|
||||
cstore: &'a mut CStore,
|
||||
used_extern_options: &'a mut FxHashSet<Symbol>,
|
||||
}
|
||||
|
||||
pub enum LoadedMacro {
|
||||
|
|
@ -239,47 +240,49 @@ impl CStore {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(sess: &Session) -> CStore {
|
||||
let mut stable_crate_ids = FxHashMap::default();
|
||||
stable_crate_ids.insert(sess.local_stable_crate_id(), LOCAL_CRATE);
|
||||
CStore {
|
||||
// We add an empty entry for LOCAL_CRATE (which maps to zero) in
|
||||
// order to make array indices in `metas` match with the
|
||||
// corresponding `CrateNum`. This first entry will always remain
|
||||
// `None`.
|
||||
metas: IndexVec::from_elem_n(None, 1),
|
||||
injected_panic_runtime: None,
|
||||
allocator_kind: None,
|
||||
alloc_error_handler_kind: None,
|
||||
has_global_allocator: false,
|
||||
has_alloc_error_handler: false,
|
||||
stable_crate_ids,
|
||||
unused_externs: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CrateLoader<'a> {
|
||||
pub fn new(
|
||||
sess: &'a Session,
|
||||
metadata_loader: Box<MetadataLoaderDyn>,
|
||||
metadata_loader: &'a MetadataLoaderDyn,
|
||||
local_crate_name: Symbol,
|
||||
cstore: &'a mut CStore,
|
||||
definitions: ReadGuard<'a, Definitions>,
|
||||
used_extern_options: &'a mut FxHashSet<Symbol>,
|
||||
) -> Self {
|
||||
let mut stable_crate_ids = FxHashMap::default();
|
||||
stable_crate_ids.insert(sess.local_stable_crate_id(), LOCAL_CRATE);
|
||||
|
||||
CrateLoader {
|
||||
sess,
|
||||
metadata_loader,
|
||||
local_crate_name,
|
||||
cstore: CStore {
|
||||
// We add an empty entry for LOCAL_CRATE (which maps to zero) in
|
||||
// order to make array indices in `metas` match with the
|
||||
// corresponding `CrateNum`. This first entry will always remain
|
||||
// `None`.
|
||||
metas: IndexVec::from_elem_n(None, 1),
|
||||
injected_panic_runtime: None,
|
||||
allocator_kind: None,
|
||||
alloc_error_handler_kind: None,
|
||||
has_global_allocator: false,
|
||||
has_alloc_error_handler: false,
|
||||
stable_crate_ids,
|
||||
unused_externs: Vec::new(),
|
||||
},
|
||||
used_extern_options: Default::default(),
|
||||
cstore,
|
||||
used_extern_options,
|
||||
definitions,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cstore(&self) -> &CStore {
|
||||
&self.cstore
|
||||
}
|
||||
|
||||
pub fn into_cstore(self) -> CStore {
|
||||
self.cstore
|
||||
}
|
||||
|
||||
fn existing_match(&self, name: Symbol, hash: Option<Svh>, kind: PathKind) -> Option<CrateNum> {
|
||||
for (cnum, data) in self.cstore.iter_crate_data() {
|
||||
if data.name() != name {
|
||||
|
|
@ -989,7 +992,6 @@ impl<'a> CrateLoader<'a> {
|
|||
pub fn process_extern_crate(
|
||||
&mut self,
|
||||
item: &ast::Item,
|
||||
definitions: &Definitions,
|
||||
def_id: LocalDefId,
|
||||
) -> Option<CrateNum> {
|
||||
match item.kind {
|
||||
|
|
@ -1013,7 +1015,7 @@ impl<'a> CrateLoader<'a> {
|
|||
|
||||
let cnum = self.resolve_crate(name, item.span, dep_kind)?;
|
||||
|
||||
let path_len = definitions.def_path(def_id).data.len();
|
||||
let path_len = self.definitions.def_path(def_id).data.len();
|
||||
self.update_extern_crate(
|
||||
cnum,
|
||||
ExternCrate {
|
||||
|
|
|
|||
|
|
@ -629,6 +629,9 @@ impl CrateStore for CStore {
|
|||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn untracked_as_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn crate_name(&self, cnum: CrateNum) -> Symbol {
|
||||
self.get_crate_data(cnum).root.name
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use rustc_index::vec::Idx;
|
|||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_span::def_id::StableCrateId;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
#[inline]
|
||||
|
|
@ -1162,7 +1162,7 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
|
|||
.filter_map(|(def_id, info)| {
|
||||
let _ = info.as_owner()?;
|
||||
let def_path_hash = definitions.def_path_hash(def_id);
|
||||
let span = resolutions.source_span.get(def_id).unwrap_or(&DUMMY_SP);
|
||||
let span = tcx.source_span(def_id);
|
||||
debug_assert_eq!(span.parent(), None);
|
||||
Some((def_path_hash, span))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -141,8 +141,6 @@ pub fn provide(providers: &mut Providers) {
|
|||
providers.hir_attrs = |tcx, id| {
|
||||
tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs)
|
||||
};
|
||||
providers.source_span =
|
||||
|tcx, def_id| tcx.resolutions(()).source_span.get(def_id).copied().unwrap_or(DUMMY_SP);
|
||||
providers.def_span = |tcx, def_id| {
|
||||
let def_id = def_id.expect_local();
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
use super::{BasicBlock, Constant, Field, Local, SwitchTargets, UserTypeProjection};
|
||||
|
||||
use crate::mir::coverage::{CodeRegion, CoverageKind};
|
||||
use crate::traits::Reveal;
|
||||
use crate::ty::adjustment::PointerCast;
|
||||
use crate::ty::subst::SubstsRef;
|
||||
use crate::ty::{self, List, Ty};
|
||||
|
|
@ -100,6 +101,13 @@ impl MirPhase {
|
|||
MirPhase::Runtime(RuntimePhase::Optimized) => "runtime-optimized",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reveal(&self) -> Reveal {
|
||||
match *self {
|
||||
MirPhase::Built | MirPhase::Analysis(_) => Reveal::UserFacing,
|
||||
MirPhase::Runtime(_) => Reveal::All,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`MirPhase::Analysis`].
|
||||
|
|
@ -518,12 +526,6 @@ pub enum TerminatorKind<'tcx> {
|
|||
SwitchInt {
|
||||
/// The discriminant value being tested.
|
||||
discr: Operand<'tcx>,
|
||||
|
||||
/// The type of value being tested.
|
||||
/// This is always the same as the type of `discr`.
|
||||
/// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing.
|
||||
switch_ty: Ty<'tcx>,
|
||||
|
||||
targets: SwitchTargets,
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
use crate::mir;
|
||||
use crate::mir::interpret::Scalar;
|
||||
use crate::ty::{self, Ty, TyCtxt};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use super::{BasicBlock, InlineAsmOperand, Operand, SourceInfo, TerminatorKind};
|
||||
|
|
@ -131,17 +128,8 @@ impl<'tcx> Terminator<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> TerminatorKind<'tcx> {
|
||||
pub fn if_(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cond: Operand<'tcx>,
|
||||
t: BasicBlock,
|
||||
f: BasicBlock,
|
||||
) -> TerminatorKind<'tcx> {
|
||||
TerminatorKind::SwitchInt {
|
||||
discr: cond,
|
||||
switch_ty: tcx.types.bool,
|
||||
targets: SwitchTargets::static_if(0, f, t),
|
||||
}
|
||||
pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
|
||||
TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
|
||||
}
|
||||
|
||||
pub fn successors(&self) -> Successors<'_> {
|
||||
|
|
@ -264,11 +252,9 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_switch(&self) -> Option<(&Operand<'tcx>, Ty<'tcx>, &SwitchTargets)> {
|
||||
pub fn as_switch(&self) -> Option<(&Operand<'tcx>, &SwitchTargets)> {
|
||||
match self {
|
||||
TerminatorKind::SwitchInt { discr, switch_ty, targets } => {
|
||||
Some((discr, *switch_ty, targets))
|
||||
}
|
||||
TerminatorKind::SwitchInt { discr, targets } => Some((discr, targets)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -403,21 +389,12 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
match *self {
|
||||
Return | Resume | Abort | Unreachable | GeneratorDrop => vec![],
|
||||
Goto { .. } => vec!["".into()],
|
||||
SwitchInt { ref targets, switch_ty, .. } => ty::tls::with(|tcx| {
|
||||
let param_env = ty::ParamEnv::empty();
|
||||
let switch_ty = tcx.lift(switch_ty).unwrap();
|
||||
let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
|
||||
targets
|
||||
.values
|
||||
.iter()
|
||||
.map(|&u| {
|
||||
mir::ConstantKind::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty)
|
||||
.to_string()
|
||||
.into()
|
||||
})
|
||||
.chain(iter::once("otherwise".into()))
|
||||
.collect()
|
||||
}),
|
||||
SwitchInt { ref targets, .. } => targets
|
||||
.values
|
||||
.iter()
|
||||
.map(|&u| Cow::Owned(u.to_string()))
|
||||
.chain(iter::once("otherwise".into()))
|
||||
.collect(),
|
||||
Call { target: Some(_), cleanup: Some(_), .. } => {
|
||||
vec!["return".into(), "unwind".into()]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -477,11 +477,9 @@ macro_rules! make_mir_visitor {
|
|||
|
||||
TerminatorKind::SwitchInt {
|
||||
discr,
|
||||
switch_ty,
|
||||
targets: _
|
||||
} => {
|
||||
self.visit_operand(discr, location);
|
||||
self.visit_ty($(& $mutability)? *switch_ty, TyContext::Location(location));
|
||||
}
|
||||
|
||||
TerminatorKind::Drop {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ rustc_queries! {
|
|||
/// This span is meant for dep-tracking rather than diagnostics. It should not be used outside
|
||||
/// of rustc_middle::hir::source_map.
|
||||
query source_span(key: LocalDefId) -> Span {
|
||||
// Accesses untracked data
|
||||
eval_always
|
||||
desc { "getting the source span" }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ use rustc_data_structures::profiling::SelfProfilerRef;
|
|||
use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::steal::Steal;
|
||||
use rustc_data_structures::sync::{self, Lock, Lrc, ReadGuard, RwLock, WorkerLocal};
|
||||
use rustc_data_structures::sync::{self, Lock, Lrc, ReadGuard, WorkerLocal};
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_data_structures::vec_map::VecMap;
|
||||
use rustc_errors::{
|
||||
|
|
@ -59,7 +59,7 @@ use rustc_query_system::dep_graph::DepNodeIndex;
|
|||
use rustc_query_system::ich::StableHashingContext;
|
||||
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
|
||||
use rustc_session::config::{CrateType, OutputFilenames};
|
||||
use rustc_session::cstore::CrateStoreDyn;
|
||||
use rustc_session::cstore::{CrateStoreDyn, Untracked};
|
||||
use rustc_session::lint::Lint;
|
||||
use rustc_session::Limit;
|
||||
use rustc_session::Session;
|
||||
|
|
@ -182,20 +182,12 @@ impl<'tcx> CtxtInterners<'tcx> {
|
|||
/// Interns a type.
|
||||
#[allow(rustc::usage_of_ty_tykind)]
|
||||
#[inline(never)]
|
||||
fn intern_ty(
|
||||
&self,
|
||||
kind: TyKind<'tcx>,
|
||||
sess: &Session,
|
||||
definitions: &rustc_hir::definitions::Definitions,
|
||||
cstore: &CrateStoreDyn,
|
||||
source_span: &IndexVec<LocalDefId, Span>,
|
||||
) -> Ty<'tcx> {
|
||||
fn intern_ty(&self, kind: TyKind<'tcx>, sess: &Session, untracked: &Untracked) -> Ty<'tcx> {
|
||||
Ty(Interned::new_unchecked(
|
||||
self.type_
|
||||
.intern(kind, |kind| {
|
||||
let flags = super::flags::FlagComputation::for_kind(&kind);
|
||||
let stable_hash =
|
||||
self.stable_hash(&flags, sess, definitions, cstore, source_span, &kind);
|
||||
let stable_hash = self.stable_hash(&flags, sess, untracked, &kind);
|
||||
|
||||
InternedInSet(self.arena.alloc(WithCachedTypeInfo {
|
||||
internee: kind,
|
||||
|
|
@ -212,9 +204,7 @@ impl<'tcx> CtxtInterners<'tcx> {
|
|||
&self,
|
||||
flags: &ty::flags::FlagComputation,
|
||||
sess: &'a Session,
|
||||
definitions: &'a rustc_hir::definitions::Definitions,
|
||||
cstore: &'a CrateStoreDyn,
|
||||
source_span: &'a IndexVec<LocalDefId, Span>,
|
||||
untracked: &'a Untracked,
|
||||
val: &T,
|
||||
) -> Fingerprint {
|
||||
// It's impossible to hash inference variables (and will ICE), so we don't need to try to cache them.
|
||||
|
|
@ -223,7 +213,7 @@ impl<'tcx> CtxtInterners<'tcx> {
|
|||
Fingerprint::ZERO
|
||||
} else {
|
||||
let mut hasher = StableHasher::new();
|
||||
let mut hcx = StableHashingContext::new(sess, definitions, cstore, source_span);
|
||||
let mut hcx = StableHashingContext::new(sess, untracked);
|
||||
val.hash_stable(&mut hcx, &mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
|
@ -234,17 +224,14 @@ impl<'tcx> CtxtInterners<'tcx> {
|
|||
&self,
|
||||
kind: Binder<'tcx, PredicateKind<'tcx>>,
|
||||
sess: &Session,
|
||||
definitions: &rustc_hir::definitions::Definitions,
|
||||
cstore: &CrateStoreDyn,
|
||||
source_span: &IndexVec<LocalDefId, Span>,
|
||||
untracked: &Untracked,
|
||||
) -> Predicate<'tcx> {
|
||||
Predicate(Interned::new_unchecked(
|
||||
self.predicate
|
||||
.intern(kind, |kind| {
|
||||
let flags = super::flags::FlagComputation::for_predicate(kind);
|
||||
|
||||
let stable_hash =
|
||||
self.stable_hash(&flags, sess, definitions, cstore, source_span, &kind);
|
||||
let stable_hash = self.stable_hash(&flags, sess, untracked, &kind);
|
||||
|
||||
InternedInSet(self.arena.alloc(WithCachedTypeInfo {
|
||||
internee: kind,
|
||||
|
|
@ -962,11 +949,9 @@ impl<'tcx> CommonTypes<'tcx> {
|
|||
fn new(
|
||||
interners: &CtxtInterners<'tcx>,
|
||||
sess: &Session,
|
||||
definitions: &rustc_hir::definitions::Definitions,
|
||||
cstore: &CrateStoreDyn,
|
||||
source_span: &IndexVec<LocalDefId, Span>,
|
||||
untracked: &Untracked,
|
||||
) -> CommonTypes<'tcx> {
|
||||
let mk = |ty| interners.intern_ty(ty, sess, definitions, cstore, source_span);
|
||||
let mk = |ty| interners.intern_ty(ty, sess, untracked);
|
||||
|
||||
CommonTypes {
|
||||
unit: mk(Tuple(List::empty())),
|
||||
|
|
@ -1112,8 +1097,7 @@ pub struct GlobalCtxt<'tcx> {
|
|||
/// Common consts, pre-interned for your convenience.
|
||||
pub consts: CommonConsts<'tcx>,
|
||||
|
||||
definitions: RwLock<Definitions>,
|
||||
|
||||
untracked: Untracked,
|
||||
/// Output of the resolver.
|
||||
pub(crate) untracked_resolutions: ty::ResolverGlobalCtxt,
|
||||
/// The entire crate as AST. This field serves as the input for the hir_crate query,
|
||||
|
|
@ -1278,8 +1262,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
lint_store: Lrc<dyn Any + sync::Send + sync::Sync>,
|
||||
arena: &'tcx WorkerLocal<Arena<'tcx>>,
|
||||
hir_arena: &'tcx WorkerLocal<hir::Arena<'tcx>>,
|
||||
definitions: Definitions,
|
||||
untracked_resolutions: ty::ResolverGlobalCtxt,
|
||||
untracked: Untracked,
|
||||
krate: Lrc<ast::Crate>,
|
||||
dep_graph: DepGraph,
|
||||
on_disk_cache: Option<&'tcx dyn OnDiskCache<'tcx>>,
|
||||
|
|
@ -1292,14 +1276,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
s.emit_fatal(err);
|
||||
});
|
||||
let interners = CtxtInterners::new(arena);
|
||||
let common_types = CommonTypes::new(
|
||||
&interners,
|
||||
s,
|
||||
&definitions,
|
||||
&*untracked_resolutions.cstore,
|
||||
// This is only used to create a stable hashing context.
|
||||
&untracked_resolutions.source_span,
|
||||
);
|
||||
let common_types = CommonTypes::new(&interners, s, &untracked);
|
||||
let common_lifetimes = CommonLifetimes::new(&interners);
|
||||
let common_consts = CommonConsts::new(&interners, &common_types);
|
||||
|
||||
|
|
@ -1310,11 +1287,11 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
hir_arena,
|
||||
interners,
|
||||
dep_graph,
|
||||
definitions: RwLock::new(definitions),
|
||||
prof: s.prof.clone(),
|
||||
types: common_types,
|
||||
lifetimes: common_lifetimes,
|
||||
consts: common_consts,
|
||||
untracked,
|
||||
untracked_resolutions,
|
||||
untracked_crate: Steal::new(krate),
|
||||
on_disk_cache,
|
||||
|
|
@ -1428,7 +1405,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
if let Some(id) = id.as_local() {
|
||||
self.definitions_untracked().def_key(id)
|
||||
} else {
|
||||
self.untracked_resolutions.cstore.def_key(id)
|
||||
self.untracked.cstore.def_key(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1442,7 +1419,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
if let Some(id) = id.as_local() {
|
||||
self.definitions_untracked().def_path(id)
|
||||
} else {
|
||||
self.untracked_resolutions.cstore.def_path(id)
|
||||
self.untracked.cstore.def_path(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1452,7 +1429,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
if let Some(def_id) = def_id.as_local() {
|
||||
self.definitions_untracked().def_path_hash(def_id)
|
||||
} else {
|
||||
self.untracked_resolutions.cstore.def_path_hash(def_id)
|
||||
self.untracked.cstore.def_path_hash(def_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1461,7 +1438,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
if crate_num == LOCAL_CRATE {
|
||||
self.sess.local_stable_crate_id()
|
||||
} else {
|
||||
self.untracked_resolutions.cstore.stable_crate_id(crate_num)
|
||||
self.untracked.cstore.stable_crate_id(crate_num)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1472,7 +1449,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
if stable_crate_id == self.sess.local_stable_crate_id() {
|
||||
LOCAL_CRATE
|
||||
} else {
|
||||
self.untracked_resolutions.cstore.stable_crate_id_to_crate_num(stable_crate_id)
|
||||
self.untracked.cstore.stable_crate_id_to_crate_num(stable_crate_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1487,11 +1464,11 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
// If this is a DefPathHash from the local crate, we can look up the
|
||||
// DefId in the tcx's `Definitions`.
|
||||
if stable_crate_id == self.sess.local_stable_crate_id() {
|
||||
self.definitions.read().local_def_path_hash_to_def_id(hash, err).to_def_id()
|
||||
self.untracked.definitions.read().local_def_path_hash_to_def_id(hash, err).to_def_id()
|
||||
} else {
|
||||
// If this is a DefPathHash from an upstream crate, let the CrateStore map
|
||||
// it to a DefId.
|
||||
let cstore = &*self.untracked_resolutions.cstore;
|
||||
let cstore = &*self.untracked.cstore;
|
||||
let cnum = cstore.stable_crate_id_to_crate_num(stable_crate_id);
|
||||
cstore.def_path_hash_to_def_id(cnum, hash)
|
||||
}
|
||||
|
|
@ -1505,7 +1482,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
let (crate_name, stable_crate_id) = if def_id.is_local() {
|
||||
(self.crate_name, self.sess.local_stable_crate_id())
|
||||
} else {
|
||||
let cstore = &*self.untracked_resolutions.cstore;
|
||||
let cstore = &*self.untracked.cstore;
|
||||
(cstore.crate_name(def_id.krate), cstore.stable_crate_id(def_id.krate))
|
||||
};
|
||||
|
||||
|
|
@ -1547,7 +1524,7 @@ impl<'tcx> TyCtxtAt<'tcx> {
|
|||
// This is fine because:
|
||||
// - those queries are `eval_always` so we won't miss their result changing;
|
||||
// - this write will have happened before these queries are called.
|
||||
let key = self.definitions.write().create_def(parent, data);
|
||||
let key = self.untracked.definitions.write().create_def(parent, data);
|
||||
|
||||
let feed = TyCtxtFeed { tcx: self.tcx, key };
|
||||
feed.def_span(self.span);
|
||||
|
|
@ -1561,7 +1538,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
// definitions change.
|
||||
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
|
||||
|
||||
let definitions = &self.definitions;
|
||||
let definitions = &self.untracked.definitions;
|
||||
std::iter::from_generator(|| {
|
||||
let mut i = 0;
|
||||
|
||||
|
|
@ -1585,7 +1562,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
|
||||
// Leak a read lock once we start iterating on definitions, to prevent adding new ones
|
||||
// while iterating. If some query needs to add definitions, it should be `ensure`d above.
|
||||
let definitions = self.definitions.leak();
|
||||
let definitions = self.untracked.definitions.leak();
|
||||
definitions.def_path_table()
|
||||
}
|
||||
|
||||
|
|
@ -1597,28 +1574,28 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self.ensure().hir_crate(());
|
||||
// Leak a read lock once we start iterating on definitions, to prevent adding new ones
|
||||
// while iterating. If some query needs to add definitions, it should be `ensure`d above.
|
||||
let definitions = self.definitions.leak();
|
||||
let definitions = self.untracked.definitions.leak();
|
||||
definitions.def_path_hash_to_def_index_map()
|
||||
}
|
||||
|
||||
/// Note that this is *untracked* and should only be used within the query
|
||||
/// system if the result is otherwise tracked through queries
|
||||
pub fn cstore_untracked(self) -> &'tcx CrateStoreDyn {
|
||||
&*self.untracked_resolutions.cstore
|
||||
&*self.untracked.cstore
|
||||
}
|
||||
|
||||
/// Note that this is *untracked* and should only be used within the query
|
||||
/// system if the result is otherwise tracked through queries
|
||||
#[inline]
|
||||
pub fn definitions_untracked(self) -> ReadGuard<'tcx, Definitions> {
|
||||
self.definitions.read()
|
||||
self.untracked.definitions.read()
|
||||
}
|
||||
|
||||
/// Note that this is *untracked* and should only be used within the query
|
||||
/// system if the result is otherwise tracked through queries
|
||||
#[inline]
|
||||
pub fn source_span_untracked(self, def_id: LocalDefId) -> Span {
|
||||
self.untracked_resolutions.source_span.get(def_id).copied().unwrap_or(DUMMY_SP)
|
||||
self.untracked.source_span.get(def_id).copied().unwrap_or(DUMMY_SP)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
@ -1626,14 +1603,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self,
|
||||
f: impl FnOnce(StableHashingContext<'_>) -> R,
|
||||
) -> R {
|
||||
let definitions = self.definitions_untracked();
|
||||
let hcx = StableHashingContext::new(
|
||||
self.sess,
|
||||
&*definitions,
|
||||
&*self.untracked_resolutions.cstore,
|
||||
&self.untracked_resolutions.source_span,
|
||||
);
|
||||
f(hcx)
|
||||
f(StableHashingContext::new(self.sess, &self.untracked))
|
||||
}
|
||||
|
||||
pub fn serialize_query_result_cache(self, encoder: FileEncoder) -> FileEncodeResult {
|
||||
|
|
@ -2427,10 +2397,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self.interners.intern_ty(
|
||||
st,
|
||||
self.sess,
|
||||
&self.definitions.read(),
|
||||
&*self.untracked_resolutions.cstore,
|
||||
// This is only used to create a stable hashing context.
|
||||
&self.untracked_resolutions.source_span,
|
||||
&self.untracked,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -2439,10 +2407,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self.interners.intern_predicate(
|
||||
binder,
|
||||
self.sess,
|
||||
&self.definitions.read(),
|
||||
&*self.untracked_resolutions.cstore,
|
||||
// This is only used to create a stable hashing context.
|
||||
&self.untracked_resolutions.source_span,
|
||||
&self.untracked,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -3124,4 +3090,6 @@ pub fn provide(providers: &mut ty::query::Providers) {
|
|||
// We want to check if the panic handler was defined in this crate
|
||||
tcx.lang_items().panic_impl().map_or(false, |did| did.is_local())
|
||||
};
|
||||
providers.source_span =
|
||||
|tcx, def_id| tcx.untracked.source_span.get(def_id).copied().unwrap_or(DUMMY_SP);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,6 +234,15 @@ impl<'tcx> Generics {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn params_to(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx [GenericParamDef] {
|
||||
if let Some(index) = param_index.checked_sub(self.parent_count) {
|
||||
&self.params[..index]
|
||||
} else {
|
||||
tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?"))
|
||||
.params_to(param_index, tcx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `GenericParamDef` associated with this `EarlyBoundRegion`.
|
||||
pub fn region_param(
|
||||
&'tcx self,
|
||||
|
|
|
|||
|
|
@ -40,13 +40,12 @@ use rustc_data_structures::tagged_ptr::CopyTaggedPtr;
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, LifetimeRes, Res};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap};
|
||||
use rustc_hir::definitions::Definitions;
|
||||
use rustc_hir::Node;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_query_system::ich::StableHashingContext;
|
||||
use rustc_serialize::{Decodable, Encodable};
|
||||
use rustc_session::cstore::CrateStoreDyn;
|
||||
use rustc_session::cstore::Untracked;
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{ExpnId, Span};
|
||||
|
|
@ -150,21 +149,18 @@ mod sty;
|
|||
pub type RegisteredTools = FxHashSet<Ident>;
|
||||
|
||||
pub struct ResolverOutputs {
|
||||
pub definitions: Definitions,
|
||||
pub global_ctxt: ResolverGlobalCtxt,
|
||||
pub ast_lowering: ResolverAstLowering,
|
||||
pub untracked: Untracked,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ResolverGlobalCtxt {
|
||||
pub cstore: Box<CrateStoreDyn>,
|
||||
pub visibilities: FxHashMap<LocalDefId, Visibility>,
|
||||
/// This field is used to decide whether we should make `PRIVATE_IN_PUBLIC` a hard error.
|
||||
pub has_pub_restricted: bool,
|
||||
/// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
|
||||
pub expn_that_defined: FxHashMap<LocalDefId, ExpnId>,
|
||||
/// Reference span for definitions.
|
||||
pub source_span: IndexVec<LocalDefId, Span>,
|
||||
pub effective_visibilities: EffectiveVisibilities,
|
||||
pub extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
|
||||
pub maybe_unused_trait_imports: FxIndexSet<LocalDefId>,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
|
||||
|
||||
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use crate::mir;
|
||||
use crate::ty::layout::IntegerExt;
|
||||
use crate::ty::{
|
||||
self, DefIdTree, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
||||
|
|
@ -15,6 +16,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::bit_set::GrowableBitSet;
|
||||
use rustc_index::vec::{Idx, IndexVec};
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_span::{sym, DUMMY_SP};
|
||||
use rustc_target::abi::{Integer, IntegerType, Size, TargetDataLayout};
|
||||
|
|
@ -692,6 +694,80 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
pub fn bound_impl_subject(self, def_id: DefId) -> ty::EarlyBinder<ty::ImplSubject<'tcx>> {
|
||||
ty::EarlyBinder(self.impl_subject(def_id))
|
||||
}
|
||||
|
||||
/// Returns names of captured upvars for closures and generators.
|
||||
///
|
||||
/// Here are some examples:
|
||||
/// - `name__field1__field2` when the upvar is captured by value.
|
||||
/// - `_ref__name__field` when the upvar is captured by reference.
|
||||
///
|
||||
/// For generators this only contains upvars that are shared by all states.
|
||||
pub fn closure_saved_names_of_captured_variables(
|
||||
self,
|
||||
def_id: DefId,
|
||||
) -> SmallVec<[String; 16]> {
|
||||
let body = self.optimized_mir(def_id);
|
||||
|
||||
body.var_debug_info
|
||||
.iter()
|
||||
.filter_map(|var| {
|
||||
let is_ref = match var.value {
|
||||
mir::VarDebugInfoContents::Place(place)
|
||||
if place.local == mir::Local::new(1) =>
|
||||
{
|
||||
// The projection is either `[.., Field, Deref]` or `[.., Field]`. It
|
||||
// implies whether the variable is captured by value or by reference.
|
||||
matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let prefix = if is_ref { "_ref__" } else { "" };
|
||||
Some(prefix.to_owned() + var.name.as_str())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// FIXME(eddyb) maybe precompute this? Right now it's computed once
|
||||
// per generator monomorphization, but it doesn't depend on substs.
|
||||
pub fn generator_layout_and_saved_local_names(
|
||||
self,
|
||||
def_id: DefId,
|
||||
) -> (
|
||||
&'tcx ty::GeneratorLayout<'tcx>,
|
||||
IndexVec<mir::GeneratorSavedLocal, Option<rustc_span::Symbol>>,
|
||||
) {
|
||||
let tcx = self;
|
||||
let body = tcx.optimized_mir(def_id);
|
||||
let generator_layout = body.generator_layout().unwrap();
|
||||
let mut generator_saved_local_names =
|
||||
IndexVec::from_elem(None, &generator_layout.field_tys);
|
||||
|
||||
let state_arg = mir::Local::new(1);
|
||||
for var in &body.var_debug_info {
|
||||
let mir::VarDebugInfoContents::Place(place) = &var.value else { continue };
|
||||
if place.local != state_arg {
|
||||
continue;
|
||||
}
|
||||
match place.projection[..] {
|
||||
[
|
||||
// Deref of the `Pin<&mut Self>` state argument.
|
||||
mir::ProjectionElem::Field(..),
|
||||
mir::ProjectionElem::Deref,
|
||||
// Field of a variant of the state.
|
||||
mir::ProjectionElem::Downcast(_, variant),
|
||||
mir::ProjectionElem::Field(field, _),
|
||||
] => {
|
||||
let name = &mut generator_saved_local_names
|
||||
[generator_layout.variant_fields[variant][field]];
|
||||
if name.is_none() {
|
||||
name.replace(var.name);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
(generator_layout, generator_saved_local_names)
|
||||
}
|
||||
}
|
||||
|
||||
struct OpaqueTypeExpander<'tcx> {
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
LogicalOp::And => (else_block, shortcircuit_block),
|
||||
LogicalOp::Or => (shortcircuit_block, else_block),
|
||||
};
|
||||
let term = TerminatorKind::if_(this.tcx, lhs, blocks.0, blocks.1);
|
||||
let term = TerminatorKind::if_(lhs, blocks.0, blocks.1);
|
||||
this.cfg.terminate(block, source_info, term);
|
||||
|
||||
this.cfg.push_assign_constant(
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
let then_block = this.cfg.start_new_block();
|
||||
let else_block = this.cfg.start_new_block();
|
||||
let term = TerminatorKind::if_(this.tcx, operand, then_block, else_block);
|
||||
let term = TerminatorKind::if_(operand, then_block, else_block);
|
||||
|
||||
let source_info = this.source_info(expr_span);
|
||||
this.cfg.terminate(block, source_info, term);
|
||||
|
|
|
|||
|
|
@ -203,7 +203,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
self.source_info(match_start_span),
|
||||
TerminatorKind::SwitchInt {
|
||||
discr: Operand::Move(discr),
|
||||
switch_ty: discr_ty,
|
||||
targets: switch_targets,
|
||||
},
|
||||
);
|
||||
|
|
@ -221,7 +220,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
0 => (second_bb, first_bb),
|
||||
v => span_bug!(test.span, "expected boolean value but got {:?}", v),
|
||||
};
|
||||
TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb)
|
||||
TerminatorKind::if_(Operand::Copy(place), true_bb, false_bb)
|
||||
} else {
|
||||
// The switch may be inexhaustive so we have a catch all block
|
||||
debug_assert_eq!(options.len() + 1, target_blocks.len());
|
||||
|
|
@ -232,7 +231,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
);
|
||||
TerminatorKind::SwitchInt {
|
||||
discr: Operand::Copy(place),
|
||||
switch_ty,
|
||||
targets: switch_targets,
|
||||
}
|
||||
};
|
||||
|
|
@ -378,7 +376,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
self.cfg.terminate(
|
||||
block,
|
||||
source_info,
|
||||
TerminatorKind::if_(self.tcx, Operand::Move(result), success_block, fail_block),
|
||||
TerminatorKind::if_(Operand::Move(result), success_block, fail_block),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -482,7 +480,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
self.cfg.terminate(
|
||||
eq_block,
|
||||
source_info,
|
||||
TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block),
|
||||
TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -596,7 +596,6 @@ where
|
|||
source_info: self.source_info,
|
||||
kind: TerminatorKind::SwitchInt {
|
||||
discr: Operand::Move(discr),
|
||||
switch_ty: discr_ty,
|
||||
targets: SwitchTargets::new(
|
||||
values.iter().copied().zip(blocks.iter().copied()),
|
||||
*blocks.last().unwrap(),
|
||||
|
|
@ -716,7 +715,7 @@ where
|
|||
is_cleanup: unwind.is_cleanup(),
|
||||
terminator: Some(Terminator {
|
||||
source_info: self.source_info,
|
||||
kind: TerminatorKind::if_(tcx, move_(can_go), succ, drop_block),
|
||||
kind: TerminatorKind::if_(move_(can_go), succ, drop_block),
|
||||
}),
|
||||
};
|
||||
let loop_block = self.elaborator.patch().new_block(loop_block);
|
||||
|
|
@ -781,7 +780,6 @@ where
|
|||
source_info: self.source_info,
|
||||
kind: TerminatorKind::SwitchInt {
|
||||
discr: move_(elem_size),
|
||||
switch_ty: tcx.types.usize,
|
||||
targets: SwitchTargets::static_if(
|
||||
0,
|
||||
self.drop_loop_pair(ety, false, len),
|
||||
|
|
@ -1021,7 +1019,7 @@ where
|
|||
DropStyle::Static => on_set,
|
||||
DropStyle::Conditional | DropStyle::Open => {
|
||||
let flag = self.elaborator.get_drop_flag(self.path).unwrap();
|
||||
let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset);
|
||||
let term = TerminatorKind::if_(flag, on_set, on_unset);
|
||||
self.new_block(unwind, term)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ impl Direction for Backward {
|
|||
propagate(pred, &tmp);
|
||||
}
|
||||
|
||||
mir::TerminatorKind::SwitchInt { targets: _, ref discr, switch_ty: _ } => {
|
||||
mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
|
||||
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
|
||||
body,
|
||||
pred,
|
||||
|
|
@ -577,7 +577,7 @@ impl Direction for Forward {
|
|||
}
|
||||
}
|
||||
|
||||
SwitchInt { ref targets, ref discr, switch_ty: _ } => {
|
||||
SwitchInt { ref targets, ref discr } => {
|
||||
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
|
||||
exit_state,
|
||||
targets,
|
||||
|
|
|
|||
|
|
@ -82,8 +82,9 @@ impl<'tcx> Visitor<'tcx> for ConstGotoOptimizationFinder<'_, 'tcx> {
|
|||
}
|
||||
|
||||
let target_bb_terminator = target_bb.terminator();
|
||||
let (discr, switch_ty, targets) = target_bb_terminator.kind.as_switch()?;
|
||||
let (discr, targets) = target_bb_terminator.kind.as_switch()?;
|
||||
if discr.place() == Some(*place) {
|
||||
let switch_ty = place.ty(self.body.local_decls(), self.tcx).ty;
|
||||
// We now know that the Switch matches on the const place, and it is statementless
|
||||
// Now find which value in the Switch matches the const value.
|
||||
let const_value =
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ use rustc_data_structures::graph::WithSuccessors;
|
|||
use rustc_index::vec::{Idx, IndexVec};
|
||||
use rustc_middle::mir::coverage::CoverageKind;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP};
|
||||
|
||||
// All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`.
|
||||
|
|
@ -47,7 +47,6 @@ struct MockBlocks<'tcx> {
|
|||
blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
dummy_place: Place<'tcx>,
|
||||
next_local: usize,
|
||||
bool_ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> MockBlocks<'tcx> {
|
||||
|
|
@ -56,7 +55,6 @@ impl<'tcx> MockBlocks<'tcx> {
|
|||
blocks: IndexVec::new(),
|
||||
dummy_place: Place { local: RETURN_PLACE, projection: ty::List::empty() },
|
||||
next_local: 0,
|
||||
bool_ty: TyCtxt::BOOL_TY_FOR_UNIT_TESTING,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +155,6 @@ impl<'tcx> MockBlocks<'tcx> {
|
|||
fn switchint(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
|
||||
let switchint_kind = TerminatorKind::SwitchInt {
|
||||
discr: Operand::Move(Place::from(self.new_temp())),
|
||||
switch_ty: self.bool_ty, // just a dummy value
|
||||
targets: SwitchTargets::static_if(0, TEMP_BLOCK, TEMP_BLOCK),
|
||||
};
|
||||
self.add_block_from(some_from_block, switchint_kind)
|
||||
|
|
|
|||
|
|
@ -121,7 +121,6 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
|
|||
|
||||
let TerminatorKind::SwitchInt {
|
||||
discr: parent_op,
|
||||
switch_ty: parent_ty,
|
||||
targets: parent_targets
|
||||
} = &bbs[parent].terminator().kind else {
|
||||
unreachable!()
|
||||
|
|
@ -132,6 +131,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
|
|||
Operand::Copy(x) => Operand::Copy(*x),
|
||||
Operand::Constant(x) => Operand::Constant(x.clone()),
|
||||
};
|
||||
let parent_ty = parent_op.ty(body.local_decls(), tcx);
|
||||
let statements_before = bbs[parent].statements.len();
|
||||
let parent_end = Location { block: parent, statement_index: statements_before };
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
|
|||
// create temp to store inequality comparison between the two discriminants, `_t` in
|
||||
// example above
|
||||
let nequal = BinOp::Ne;
|
||||
let comp_res_type = nequal.ty(tcx, *parent_ty, opt_data.child_ty);
|
||||
let comp_res_type = nequal.ty(tcx, parent_ty, opt_data.child_ty);
|
||||
let comp_temp = patch.new_temp(comp_res_type, opt_data.child_source.span);
|
||||
patch.add_statement(parent_end, StatementKind::StorageLive(comp_temp));
|
||||
|
||||
|
|
@ -181,7 +181,6 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
|
|||
kind: TerminatorKind::SwitchInt {
|
||||
// switch on the first discriminant, so we can mark the second one as dead
|
||||
discr: parent_op,
|
||||
switch_ty: opt_data.child_ty,
|
||||
targets: eq_targets,
|
||||
},
|
||||
}));
|
||||
|
|
@ -193,12 +192,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
|
|||
let false_case = eq_bb;
|
||||
patch.patch_terminator(
|
||||
parent,
|
||||
TerminatorKind::if_(
|
||||
tcx,
|
||||
Operand::Move(Place::from(comp_temp)),
|
||||
true_case,
|
||||
false_case,
|
||||
),
|
||||
TerminatorKind::if_(Operand::Move(Place::from(comp_temp)), true_case, false_case),
|
||||
);
|
||||
|
||||
// generate StorageDead for the second_discriminant_temp not in use anymore
|
||||
|
|
@ -319,11 +313,11 @@ fn evaluate_candidate<'tcx>(
|
|||
let bbs = &body.basic_blocks;
|
||||
let TerminatorKind::SwitchInt {
|
||||
targets,
|
||||
switch_ty: parent_ty,
|
||||
..
|
||||
discr: parent_discr,
|
||||
} = &bbs[parent].terminator().kind else {
|
||||
return None
|
||||
};
|
||||
let parent_ty = parent_discr.ty(body.local_decls(), tcx);
|
||||
let parent_dest = {
|
||||
let poss = targets.otherwise();
|
||||
// If the fallthrough on the parent is trivially unreachable, we can let the
|
||||
|
|
@ -339,12 +333,12 @@ fn evaluate_candidate<'tcx>(
|
|||
let (_, child) = targets.iter().next()?;
|
||||
let child_terminator = &bbs[child].terminator();
|
||||
let TerminatorKind::SwitchInt {
|
||||
switch_ty: child_ty,
|
||||
targets: child_targets,
|
||||
..
|
||||
discr: child_discr,
|
||||
} = &child_terminator.kind else {
|
||||
return None
|
||||
};
|
||||
let child_ty = child_discr.ty(body.local_decls(), tcx);
|
||||
if child_ty != parent_ty {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -372,7 +366,7 @@ fn evaluate_candidate<'tcx>(
|
|||
Some(OptimizationData {
|
||||
destination,
|
||||
child_place: *child_place,
|
||||
child_ty: *child_ty,
|
||||
child_ty,
|
||||
child_source: child_terminator.source_info,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -877,11 +877,7 @@ fn insert_switch<'tcx>(
|
|||
let (assign, discr) = transform.get_discr(body);
|
||||
let switch_targets =
|
||||
SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block);
|
||||
let switch = TerminatorKind::SwitchInt {
|
||||
discr: Operand::Move(discr),
|
||||
switch_ty: transform.discr_ty,
|
||||
targets: switch_targets,
|
||||
};
|
||||
let switch = TerminatorKind::SwitchInt { discr: Operand::Move(discr), targets: switch_targets };
|
||||
|
||||
let source_info = SourceInfo::outermost(body.span);
|
||||
body.basic_blocks_mut().raw.insert(
|
||||
|
|
|
|||
|
|
@ -55,10 +55,9 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
continue;
|
||||
}
|
||||
|
||||
let (discr, val, switch_ty, first, second) = match bbs[bb_idx].terminator().kind {
|
||||
let (discr, val, first, second) = match bbs[bb_idx].terminator().kind {
|
||||
TerminatorKind::SwitchInt {
|
||||
discr: ref discr @ (Operand::Copy(_) | Operand::Move(_)),
|
||||
switch_ty,
|
||||
ref targets,
|
||||
..
|
||||
} if targets.iter().len() == 1 => {
|
||||
|
|
@ -66,7 +65,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
if target == targets.otherwise() {
|
||||
continue;
|
||||
}
|
||||
(discr, value, switch_ty, target, targets.otherwise())
|
||||
(discr, value, target, targets.otherwise())
|
||||
}
|
||||
// Only optimize switch int statements
|
||||
_ => continue,
|
||||
|
|
@ -105,10 +104,11 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
}
|
||||
// Take ownership of items now that we know we can optimize.
|
||||
let discr = discr.clone();
|
||||
let discr_ty = discr.ty(&body.local_decls, tcx);
|
||||
|
||||
// Introduce a temporary for the discriminant value.
|
||||
let source_info = bbs[bb_idx].terminator().source_info;
|
||||
let discr_local = body.local_decls.push(LocalDecl::new(switch_ty, source_info.span));
|
||||
let discr_local = body.local_decls.push(LocalDecl::new(discr_ty, source_info.span));
|
||||
|
||||
// We already checked that first and second are different blocks,
|
||||
// and bb_idx has a different terminator from both of them.
|
||||
|
|
@ -130,10 +130,10 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
(*f).clone()
|
||||
} else {
|
||||
// Different value between blocks. Make value conditional on switch condition.
|
||||
let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
|
||||
let size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size;
|
||||
let const_cmp = Operand::const_from_scalar(
|
||||
tcx,
|
||||
switch_ty,
|
||||
discr_ty,
|
||||
rustc_const_eval::interpret::Scalar::from_uint(val, size),
|
||||
rustc_span::DUMMY_SP,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -548,7 +548,6 @@ impl<'tcx> CloneShimBuilder<'tcx> {
|
|||
statements.push(statement);
|
||||
*kind = TerminatorKind::SwitchInt {
|
||||
discr: Operand::Move(temp),
|
||||
switch_ty: discr_ty,
|
||||
targets: SwitchTargets::new(cases.into_iter(), unreachable),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,12 +24,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyConstCondition {
|
|||
let terminator = block.terminator_mut();
|
||||
terminator.kind = match terminator.kind {
|
||||
TerminatorKind::SwitchInt {
|
||||
discr: Operand::Constant(ref c),
|
||||
switch_ty,
|
||||
ref targets,
|
||||
..
|
||||
discr: Operand::Constant(ref c), ref targets, ..
|
||||
} => {
|
||||
let constant = c.literal.try_eval_bits(tcx, param_env, switch_ty);
|
||||
let constant = c.literal.try_eval_bits(tcx, param_env, c.ty());
|
||||
if let Some(constant) = constant {
|
||||
let target = targets.target_for_value(constant);
|
||||
TerminatorKind::Goto { target }
|
||||
|
|
|
|||
|
|
@ -127,11 +127,8 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral {
|
|||
let targets = SwitchTargets::new(iter::once((new_value, bb_cond)), bb_otherwise);
|
||||
|
||||
let terminator = bb.terminator_mut();
|
||||
terminator.kind = TerminatorKind::SwitchInt {
|
||||
discr: Operand::Move(opt.to_switch_on),
|
||||
switch_ty: opt.branch_value_ty,
|
||||
targets,
|
||||
};
|
||||
terminator.kind =
|
||||
TerminatorKind::SwitchInt { discr: Operand::Move(opt.to_switch_on), targets };
|
||||
}
|
||||
|
||||
for (idx, bb_idx) in storage_deads_to_remove {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ where
|
|||
let terminator = match terminator_kind {
|
||||
// This will unconditionally run into an unreachable and is therefore unreachable as well.
|
||||
TerminatorKind::Goto { target } if is_unreachable(*target) => TerminatorKind::Unreachable,
|
||||
TerminatorKind::SwitchInt { targets, discr, switch_ty } => {
|
||||
TerminatorKind::SwitchInt { targets, discr } => {
|
||||
let otherwise = targets.otherwise();
|
||||
|
||||
// If all targets are unreachable, we can be unreachable as well.
|
||||
|
|
@ -110,11 +110,7 @@ where
|
|||
return None;
|
||||
}
|
||||
|
||||
TerminatorKind::SwitchInt {
|
||||
discr: discr.clone(),
|
||||
switch_ty: *switch_ty,
|
||||
targets: new_targets,
|
||||
}
|
||||
TerminatorKind::SwitchInt { discr: discr.clone(), targets: new_targets }
|
||||
} else {
|
||||
// If the otherwise branch is reachable, we don't want to delete any unreachable branches.
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
|
|
|
|||
|
|
@ -58,13 +58,13 @@ impl InnerOffset {
|
|||
|
||||
/// A piece is a portion of the format string which represents the next part
|
||||
/// to emit. These are emitted as a stream by the `Parser` class.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Piece<'a> {
|
||||
/// A literal string which should directly be emitted
|
||||
String(&'a str),
|
||||
/// This describes that formatting should process the next argument (as
|
||||
/// specified inside) for emission.
|
||||
NextArgument(Argument<'a>),
|
||||
NextArgument(Box<Argument<'a>>),
|
||||
}
|
||||
|
||||
/// Representation of an argument specification.
|
||||
|
|
@ -244,7 +244,7 @@ impl<'a> Iterator for Parser<'a> {
|
|||
} else {
|
||||
self.suggest_positional_arg_instead_of_captured_arg(arg);
|
||||
}
|
||||
Some(NextArgument(arg))
|
||||
Some(NextArgument(Box::new(arg)))
|
||||
}
|
||||
}
|
||||
'}' => {
|
||||
|
|
@ -908,5 +908,9 @@ fn find_skips_from_snippet(
|
|||
(skips, true)
|
||||
}
|
||||
|
||||
// Assert a reasonable size for `Piece`
|
||||
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
||||
rustc_data_structures::static_assert_size!(Piece<'_>, 16);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -76,51 +76,51 @@ fn invalid_precision() {
|
|||
fn format_nothing() {
|
||||
same(
|
||||
"{}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(0),
|
||||
position_span: InnerSpan { start: 2, end: 2 },
|
||||
format: fmtdflt(),
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn format_position() {
|
||||
same(
|
||||
"{3}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentIs(3),
|
||||
position_span: InnerSpan { start: 2, end: 3 },
|
||||
format: fmtdflt(),
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn format_position_nothing_else() {
|
||||
same(
|
||||
"{3:}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentIs(3),
|
||||
position_span: InnerSpan { start: 2, end: 3 },
|
||||
format: fmtdflt(),
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn format_named() {
|
||||
same(
|
||||
"{name}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentNamed("name"),
|
||||
position_span: InnerSpan { start: 2, end: 6 },
|
||||
format: fmtdflt(),
|
||||
})],
|
||||
}))],
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn format_type() {
|
||||
same(
|
||||
"{3:x}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentIs(3),
|
||||
position_span: InnerSpan { start: 2, end: 3 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -134,14 +134,14 @@ fn format_type() {
|
|||
ty: "x",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn format_align_fill() {
|
||||
same(
|
||||
"{3:>}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentIs(3),
|
||||
position_span: InnerSpan { start: 2, end: 3 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -155,11 +155,11 @@ fn format_align_fill() {
|
|||
ty: "",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
same(
|
||||
"{3:0<}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentIs(3),
|
||||
position_span: InnerSpan { start: 2, end: 3 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -173,11 +173,11 @@ fn format_align_fill() {
|
|||
ty: "",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
same(
|
||||
"{3:*<abcd}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentIs(3),
|
||||
position_span: InnerSpan { start: 2, end: 3 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -191,14 +191,14 @@ fn format_align_fill() {
|
|||
ty: "abcd",
|
||||
ty_span: Some(InnerSpan::new(6, 10)),
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn format_counts() {
|
||||
same(
|
||||
"{:10x}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(0),
|
||||
position_span: InnerSpan { start: 2, end: 2 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -212,11 +212,11 @@ fn format_counts() {
|
|||
ty: "x",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
same(
|
||||
"{:10$.10x}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(0),
|
||||
position_span: InnerSpan { start: 2, end: 2 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -230,11 +230,11 @@ fn format_counts() {
|
|||
ty: "x",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
same(
|
||||
"{1:0$.10x}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentIs(1),
|
||||
position_span: InnerSpan { start: 2, end: 3 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -248,11 +248,11 @@ fn format_counts() {
|
|||
ty: "x",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
same(
|
||||
"{:.*x}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(1),
|
||||
position_span: InnerSpan { start: 2, end: 2 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -266,11 +266,11 @@ fn format_counts() {
|
|||
ty: "x",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
same(
|
||||
"{:.10$x}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(0),
|
||||
position_span: InnerSpan { start: 2, end: 2 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -284,11 +284,11 @@ fn format_counts() {
|
|||
ty: "x",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
same(
|
||||
"{:a$.b$?}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(0),
|
||||
position_span: InnerSpan { start: 2, end: 2 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -302,11 +302,11 @@ fn format_counts() {
|
|||
ty: "?",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
same(
|
||||
"{:.4}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(0),
|
||||
position_span: InnerSpan { start: 2, end: 2 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -320,14 +320,14 @@ fn format_counts() {
|
|||
ty: "",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn format_flags() {
|
||||
same(
|
||||
"{:-}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(0),
|
||||
position_span: InnerSpan { start: 2, end: 2 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -341,11 +341,11 @@ fn format_flags() {
|
|||
ty: "",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
same(
|
||||
"{:+#}",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(0),
|
||||
position_span: InnerSpan { start: 2, end: 2 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -359,7 +359,7 @@ fn format_flags() {
|
|||
ty: "",
|
||||
ty_span: None,
|
||||
},
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
|
@ -368,7 +368,7 @@ fn format_mixture() {
|
|||
"abcd {3:x} efg",
|
||||
&[
|
||||
String("abcd "),
|
||||
NextArgument(Argument {
|
||||
NextArgument(Box::new(Argument {
|
||||
position: ArgumentIs(3),
|
||||
position_span: InnerSpan { start: 7, end: 8 },
|
||||
format: FormatSpec {
|
||||
|
|
@ -382,7 +382,7 @@ fn format_mixture() {
|
|||
ty: "x",
|
||||
ty_span: None,
|
||||
},
|
||||
}),
|
||||
})),
|
||||
String(" efg"),
|
||||
],
|
||||
);
|
||||
|
|
@ -391,18 +391,18 @@ fn format_mixture() {
|
|||
fn format_whitespace() {
|
||||
same(
|
||||
"{ }",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(0),
|
||||
position_span: InnerSpan { start: 2, end: 3 },
|
||||
format: fmtdflt(),
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
same(
|
||||
"{ }",
|
||||
&[NextArgument(Argument {
|
||||
&[NextArgument(Box::new(Argument {
|
||||
position: ArgumentImplicitlyIs(0),
|
||||
position_span: InnerSpan { start: 2, end: 4 },
|
||||
format: fmtdflt(),
|
||||
})],
|
||||
}))],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@ use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHa
|
|||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::definitions::{DefPathHash, Definitions};
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_session::cstore::CrateStore;
|
||||
use rustc_hir::definitions::DefPathHash;
|
||||
use rustc_session::cstore::Untracked;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
|
@ -20,9 +19,7 @@ use rustc_span::{BytePos, CachingSourceMapView, SourceFile, Span, SpanData, DUMM
|
|||
/// things (e.g., each `DefId`/`DefPath` is only hashed once).
|
||||
#[derive(Clone)]
|
||||
pub struct StableHashingContext<'a> {
|
||||
definitions: &'a Definitions,
|
||||
cstore: &'a dyn CrateStore,
|
||||
source_span: &'a IndexVec<LocalDefId, Span>,
|
||||
untracked: &'a Untracked,
|
||||
// The value of `-Z incremental-ignore-spans`.
|
||||
// This field should only be used by `unstable_opts_incremental_ignore_span`
|
||||
incremental_ignore_spans: bool,
|
||||
|
|
@ -49,19 +46,12 @@ pub(super) enum BodyResolver<'tcx> {
|
|||
|
||||
impl<'a> StableHashingContext<'a> {
|
||||
#[inline]
|
||||
pub fn new(
|
||||
sess: &'a Session,
|
||||
definitions: &'a Definitions,
|
||||
cstore: &'a dyn CrateStore,
|
||||
source_span: &'a IndexVec<LocalDefId, Span>,
|
||||
) -> Self {
|
||||
pub fn new(sess: &'a Session, untracked: &'a Untracked) -> Self {
|
||||
let hash_spans_initial = !sess.opts.unstable_opts.incremental_ignore_spans;
|
||||
|
||||
StableHashingContext {
|
||||
body_resolver: BodyResolver::Forbidden,
|
||||
definitions,
|
||||
cstore,
|
||||
source_span,
|
||||
untracked,
|
||||
incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans,
|
||||
caching_source_map: None,
|
||||
raw_source_map: sess.source_map(),
|
||||
|
|
@ -100,13 +90,13 @@ impl<'a> StableHashingContext<'a> {
|
|||
if let Some(def_id) = def_id.as_local() {
|
||||
self.local_def_path_hash(def_id)
|
||||
} else {
|
||||
self.cstore.def_path_hash(def_id)
|
||||
self.untracked.cstore.def_path_hash(def_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn local_def_path_hash(&self, def_id: LocalDefId) -> DefPathHash {
|
||||
self.definitions.def_path_hash(def_id)
|
||||
self.untracked.definitions.read().def_path_hash(def_id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -156,7 +146,7 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> {
|
|||
|
||||
#[inline]
|
||||
fn def_span(&self, def_id: LocalDefId) -> Span {
|
||||
*self.source_span.get(def_id).unwrap_or(&DUMMY_SP)
|
||||
*self.untracked.source_span.get(def_id).unwrap_or(&DUMMY_SP)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -836,12 +836,11 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
|
|||
} else if orig_name == Some(kw::SelfLower) {
|
||||
Some(self.r.graph_root)
|
||||
} else {
|
||||
self.r.crate_loader.process_extern_crate(item, &self.r.definitions, local_def_id).map(
|
||||
|crate_id| {
|
||||
self.r.extern_crate_map.insert(local_def_id, crate_id);
|
||||
self.r.expect_module(crate_id.as_def_id())
|
||||
},
|
||||
)
|
||||
let crate_id = self.r.crate_loader().process_extern_crate(item, local_def_id);
|
||||
crate_id.map(|crate_id| {
|
||||
self.r.extern_crate_map.insert(local_def_id, crate_id);
|
||||
self.r.expect_module(crate_id.as_def_id())
|
||||
})
|
||||
}
|
||||
.map(|module| {
|
||||
let used = self.process_macro_use_imports(item, module);
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ impl<'a> Resolver<'a> {
|
|||
if !candidates.is_empty() {
|
||||
show_candidates(
|
||||
&self.session,
|
||||
&self.source_span,
|
||||
&self.untracked.source_span,
|
||||
&mut err,
|
||||
span,
|
||||
&candidates,
|
||||
|
|
@ -682,7 +682,7 @@ impl<'a> Resolver<'a> {
|
|||
}
|
||||
show_candidates(
|
||||
&self.session,
|
||||
&self.source_span,
|
||||
&self.untracked.source_span,
|
||||
&mut err,
|
||||
Some(span),
|
||||
&import_suggestions,
|
||||
|
|
@ -1298,7 +1298,8 @@ impl<'a> Resolver<'a> {
|
|||
// otherwise cause duplicate suggestions.
|
||||
continue;
|
||||
}
|
||||
if let Some(crate_id) = self.crate_loader.maybe_process_path_extern(ident.name) {
|
||||
let crate_id = self.crate_loader().maybe_process_path_extern(ident.name);
|
||||
if let Some(crate_id) = crate_id {
|
||||
let crate_root = self.expect_module(crate_id.as_def_id());
|
||||
suggestions.extend(self.lookup_import_candidates_from_module(
|
||||
lookup_ident,
|
||||
|
|
@ -1335,7 +1336,7 @@ impl<'a> Resolver<'a> {
|
|||
self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);
|
||||
show_candidates(
|
||||
&self.session,
|
||||
&self.source_span,
|
||||
&self.untracked.source_span,
|
||||
err,
|
||||
None,
|
||||
&import_suggestions,
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
|
|||
r.effective_visibilities.update_eff_vis(
|
||||
r.local_def_id(node_id),
|
||||
eff_vis,
|
||||
ResolverTree(&r.definitions, &r.crate_loader),
|
||||
ResolverTree(&r.untracked),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -541,7 +541,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
|||
if let Some(candidate) = &err.candidate {
|
||||
import_candidates(
|
||||
self.r.session,
|
||||
&self.r.source_span,
|
||||
&self.r.untracked.source_span,
|
||||
&mut diag,
|
||||
Some(err.span),
|
||||
&candidate,
|
||||
|
|
|
|||
|
|
@ -1663,8 +1663,10 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
if !module.no_implicit_prelude {
|
||||
let extern_prelude = self.r.extern_prelude.clone();
|
||||
names.extend(extern_prelude.iter().flat_map(|(ident, _)| {
|
||||
self.r.crate_loader.maybe_process_path_extern(ident.name).and_then(
|
||||
|crate_id| {
|
||||
self.r
|
||||
.crate_loader()
|
||||
.maybe_process_path_extern(ident.name)
|
||||
.and_then(|crate_id| {
|
||||
let crate_mod =
|
||||
Res::Def(DefKind::Mod, crate_id.as_def_id());
|
||||
|
||||
|
|
@ -1673,8 +1675,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
}));
|
||||
|
||||
if let Some(prelude) = self.r.prelude {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use rustc_ast::{self as ast, NodeId, CRATE_NODE_ID};
|
|||
use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::sync::{Lrc, RwLock};
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind};
|
||||
use rustc_hir::def::Namespace::*;
|
||||
|
|
@ -46,7 +46,7 @@ use rustc_middle::span_bug;
|
|||
use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools};
|
||||
use rustc_middle::ty::{ResolverGlobalCtxt, ResolverOutputs};
|
||||
use rustc_query_system::ich::StableHashingContext;
|
||||
use rustc_session::cstore::{CrateStore, MetadataLoaderDyn};
|
||||
use rustc_session::cstore::{CrateStore, MetadataLoaderDyn, Untracked};
|
||||
use rustc_session::lint::LintBuffer;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency};
|
||||
|
|
@ -866,11 +866,8 @@ struct MacroData {
|
|||
pub struct Resolver<'a> {
|
||||
session: &'a Session,
|
||||
|
||||
definitions: Definitions,
|
||||
/// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
|
||||
expn_that_defined: FxHashMap<LocalDefId, ExpnId>,
|
||||
/// Reference span for definitions.
|
||||
source_span: IndexVec<LocalDefId, Span>,
|
||||
|
||||
graph_root: Module<'a>,
|
||||
|
||||
|
|
@ -954,7 +951,10 @@ pub struct Resolver<'a> {
|
|||
arenas: &'a ResolverArenas<'a>,
|
||||
dummy_binding: &'a NameBinding<'a>,
|
||||
|
||||
crate_loader: CrateLoader<'a>,
|
||||
local_crate_name: Symbol,
|
||||
metadata_loader: Box<MetadataLoaderDyn>,
|
||||
untracked: Untracked,
|
||||
used_extern_options: FxHashSet<Symbol>,
|
||||
macro_names: FxHashSet<Ident>,
|
||||
builtin_macros: FxHashMap<Symbol, BuiltinMacroState>,
|
||||
/// A small map keeping true kinds of built-in macros that appear to be fn-like on
|
||||
|
|
@ -1112,15 +1112,15 @@ impl<'a> AsMut<Resolver<'a>> for Resolver<'a> {
|
|||
/// A minimal subset of resolver that can implemenent `DefIdTree`, sometimes
|
||||
/// required to satisfy borrow checker by avoiding borrowing the whole resolver.
|
||||
#[derive(Clone, Copy)]
|
||||
struct ResolverTree<'a, 'b>(&'a Definitions, &'a CrateLoader<'b>);
|
||||
struct ResolverTree<'a>(&'a Untracked);
|
||||
|
||||
impl DefIdTree for ResolverTree<'_, '_> {
|
||||
impl DefIdTree for ResolverTree<'_> {
|
||||
#[inline]
|
||||
fn opt_parent(self, id: DefId) -> Option<DefId> {
|
||||
let ResolverTree(definitions, crate_loader) = self;
|
||||
let ResolverTree(Untracked { definitions, cstore, .. }) = self;
|
||||
match id.as_local() {
|
||||
Some(id) => definitions.def_key(id).parent,
|
||||
None => crate_loader.cstore().def_key(id).parent,
|
||||
Some(id) => definitions.read().def_key(id).parent,
|
||||
None => cstore.as_any().downcast_ref::<CStore>().unwrap().def_key(id).parent,
|
||||
}
|
||||
.map(|index| DefId { index, ..id })
|
||||
}
|
||||
|
|
@ -1129,7 +1129,7 @@ impl DefIdTree for ResolverTree<'_, '_> {
|
|||
impl<'a, 'b> DefIdTree for &'a Resolver<'b> {
|
||||
#[inline]
|
||||
fn opt_parent(self, id: DefId) -> Option<DefId> {
|
||||
ResolverTree(&self.definitions, &self.crate_loader).opt_parent(id)
|
||||
ResolverTree(&self.untracked).opt_parent(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1156,10 +1156,10 @@ impl Resolver<'_> {
|
|||
"adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}",
|
||||
node_id,
|
||||
data,
|
||||
self.definitions.def_key(self.node_id_to_def_id[&node_id]),
|
||||
self.untracked.definitions.read().def_key(self.node_id_to_def_id[&node_id]),
|
||||
);
|
||||
|
||||
let def_id = self.definitions.create_def(parent, data);
|
||||
let def_id = self.untracked.definitions.write().create_def(parent, data);
|
||||
|
||||
// Create the definition.
|
||||
if expn_id != ExpnId::root() {
|
||||
|
|
@ -1168,7 +1168,7 @@ impl Resolver<'_> {
|
|||
|
||||
// A relative span's parent must be an absolute span.
|
||||
debug_assert_eq!(span.data_untracked().parent, None);
|
||||
let _id = self.source_span.push(span);
|
||||
let _id = self.untracked.source_span.push(span);
|
||||
debug_assert_eq!(_id, def_id);
|
||||
|
||||
// Some things for which we allocate `LocalDefId`s don't correspond to
|
||||
|
|
@ -1258,9 +1258,7 @@ impl<'a> Resolver<'a> {
|
|||
let mut resolver = Resolver {
|
||||
session,
|
||||
|
||||
definitions,
|
||||
expn_that_defined: Default::default(),
|
||||
source_span,
|
||||
|
||||
// The outermost module has def ID 0; this is not reflected in the
|
||||
// AST.
|
||||
|
|
@ -1311,7 +1309,14 @@ impl<'a> Resolver<'a> {
|
|||
vis: ty::Visibility::Public,
|
||||
}),
|
||||
|
||||
crate_loader: CrateLoader::new(session, metadata_loader, crate_name),
|
||||
metadata_loader,
|
||||
local_crate_name: crate_name,
|
||||
used_extern_options: Default::default(),
|
||||
untracked: Untracked {
|
||||
cstore: Box::new(CStore::new(session)),
|
||||
source_span,
|
||||
definitions: RwLock::new(definitions),
|
||||
},
|
||||
macro_names: FxHashSet::default(),
|
||||
builtin_macros: Default::default(),
|
||||
builtin_macro_kinds: Default::default(),
|
||||
|
|
@ -1402,9 +1407,6 @@ impl<'a> Resolver<'a> {
|
|||
|
||||
pub fn into_outputs(self) -> ResolverOutputs {
|
||||
let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect();
|
||||
let definitions = self.definitions;
|
||||
let cstore = Box::new(self.crate_loader.into_cstore());
|
||||
let source_span = self.source_span;
|
||||
let expn_that_defined = self.expn_that_defined;
|
||||
let visibilities = self.visibilities;
|
||||
let has_pub_restricted = self.has_pub_restricted;
|
||||
|
|
@ -1416,9 +1418,8 @@ impl<'a> Resolver<'a> {
|
|||
let main_def = self.main_def;
|
||||
let confused_type_with_std_module = self.confused_type_with_std_module;
|
||||
let effective_visibilities = self.effective_visibilities;
|
||||
let untracked = self.untracked;
|
||||
let global_ctxt = ResolverGlobalCtxt {
|
||||
cstore,
|
||||
source_span,
|
||||
expn_that_defined,
|
||||
visibilities,
|
||||
has_pub_restricted,
|
||||
|
|
@ -1453,16 +1454,16 @@ impl<'a> Resolver<'a> {
|
|||
builtin_macro_kinds: self.builtin_macro_kinds,
|
||||
lifetime_elision_allowed: self.lifetime_elision_allowed,
|
||||
};
|
||||
ResolverOutputs { definitions, global_ctxt, ast_lowering }
|
||||
ResolverOutputs { global_ctxt, ast_lowering, untracked }
|
||||
}
|
||||
|
||||
pub fn clone_outputs(&self) -> ResolverOutputs {
|
||||
let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect();
|
||||
let definitions = self.definitions.clone();
|
||||
let definitions = self.untracked.definitions.clone();
|
||||
let cstore = Box::new(self.cstore().clone());
|
||||
let untracked =
|
||||
Untracked { cstore, source_span: self.untracked.source_span.clone(), definitions };
|
||||
let global_ctxt = ResolverGlobalCtxt {
|
||||
cstore,
|
||||
source_span: self.source_span.clone(),
|
||||
expn_that_defined: self.expn_that_defined.clone(),
|
||||
visibilities: self.visibilities.clone(),
|
||||
has_pub_restricted: self.has_pub_restricted,
|
||||
|
|
@ -1497,20 +1498,26 @@ impl<'a> Resolver<'a> {
|
|||
builtin_macro_kinds: self.builtin_macro_kinds.clone(),
|
||||
lifetime_elision_allowed: self.lifetime_elision_allowed.clone(),
|
||||
};
|
||||
ResolverOutputs { definitions, global_ctxt, ast_lowering }
|
||||
ResolverOutputs { global_ctxt, ast_lowering, untracked }
|
||||
}
|
||||
|
||||
fn create_stable_hashing_context(&self) -> StableHashingContext<'_> {
|
||||
StableHashingContext::new(
|
||||
self.session,
|
||||
&self.definitions,
|
||||
self.crate_loader.cstore(),
|
||||
&self.source_span,
|
||||
StableHashingContext::new(self.session, &self.untracked)
|
||||
}
|
||||
|
||||
pub fn crate_loader(&mut self) -> CrateLoader<'_> {
|
||||
CrateLoader::new(
|
||||
&self.session,
|
||||
&*self.metadata_loader,
|
||||
self.local_crate_name,
|
||||
&mut *self.untracked.cstore.untracked_as_any().downcast_mut().unwrap(),
|
||||
self.untracked.definitions.read(),
|
||||
&mut self.used_extern_options,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cstore(&self) -> &CStore {
|
||||
self.crate_loader.cstore()
|
||||
self.untracked.cstore.as_any().downcast_ref().unwrap()
|
||||
}
|
||||
|
||||
fn dummy_ext(&self, macro_kind: MacroKind) -> Lrc<SyntaxExtension> {
|
||||
|
|
@ -1553,7 +1560,7 @@ impl<'a> Resolver<'a> {
|
|||
self.session.time("resolve_main", || self.resolve_main());
|
||||
self.session.time("resolve_check_unused", || self.check_unused(krate));
|
||||
self.session.time("resolve_report_errors", || self.report_errors(krate));
|
||||
self.session.time("resolve_postprocess", || self.crate_loader.postprocess(krate));
|
||||
self.session.time("resolve_postprocess", || self.crate_loader().postprocess(krate));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1871,10 +1878,10 @@ impl<'a> Resolver<'a> {
|
|||
} else {
|
||||
let crate_id = if finalize {
|
||||
let Some(crate_id) =
|
||||
self.crate_loader.process_path_extern(ident.name, ident.span) else { return Some(self.dummy_binding); };
|
||||
self.crate_loader().process_path_extern(ident.name, ident.span) else { return Some(self.dummy_binding); };
|
||||
crate_id
|
||||
} else {
|
||||
self.crate_loader.maybe_process_path_extern(ident.name)?
|
||||
self.crate_loader().maybe_process_path_extern(ident.name)?
|
||||
};
|
||||
let crate_root = self.expect_module(crate_id.as_def_id());
|
||||
let vis = ty::Visibility::<LocalDefId>::Public;
|
||||
|
|
@ -1946,14 +1953,14 @@ impl<'a> Resolver<'a> {
|
|||
/// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
|
||||
#[inline]
|
||||
pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
|
||||
def_id.as_local().map(|def_id| self.source_span[def_id])
|
||||
def_id.as_local().map(|def_id| self.untracked.source_span[def_id])
|
||||
}
|
||||
|
||||
/// Retrieves the name of the given `DefId`.
|
||||
#[inline]
|
||||
pub fn opt_name(&self, def_id: DefId) -> Option<Symbol> {
|
||||
let def_key = match def_id.as_local() {
|
||||
Some(def_id) => self.definitions.def_key(def_id),
|
||||
Some(def_id) => self.untracked.definitions.read().def_key(def_id),
|
||||
None => self.cstore().def_key(def_id),
|
||||
};
|
||||
def_key.get_opt_name()
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ impl<'a> ResolverExpand for Resolver<'a> {
|
|||
}
|
||||
|
||||
fn get_proc_macro_quoted_span(&self, krate: CrateNum, id: usize) -> Span {
|
||||
self.crate_loader.cstore().get_proc_macro_quoted_span_untracked(krate, id, self.session)
|
||||
self.cstore().get_proc_macro_quoted_span_untracked(krate, id, self.session)
|
||||
}
|
||||
|
||||
fn declare_proc_macro(&mut self, id: NodeId) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ rustc_hir = { path = "../rustc_hir" }
|
|||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_fs_util = { path = "../rustc_fs_util" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub enum SizeKind {
|
|||
Min,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct FieldInfo {
|
||||
pub name: Symbol,
|
||||
pub offset: u64,
|
||||
|
|
@ -33,6 +33,7 @@ pub enum DataTypeKind {
|
|||
Union,
|
||||
Enum,
|
||||
Closure,
|
||||
Generator,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||
|
|
@ -114,7 +115,7 @@ impl CodeStats {
|
|||
|
||||
let struct_like = match kind {
|
||||
DataTypeKind::Struct | DataTypeKind::Closure => true,
|
||||
DataTypeKind::Enum | DataTypeKind::Union => false,
|
||||
DataTypeKind::Enum | DataTypeKind::Union | DataTypeKind::Generator => false,
|
||||
};
|
||||
for (i, variant_info) in variants.into_iter().enumerate() {
|
||||
let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ use crate::search_paths::PathKind;
|
|||
use crate::utils::NativeLibKind;
|
||||
use crate::Session;
|
||||
use rustc_ast as ast;
|
||||
use rustc_data_structures::sync::{self, MetadataRef};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, StableCrateId, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
|
||||
use rustc_data_structures::sync::{self, MetadataRef, RwLock};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, StableCrateId, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash, Definitions};
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_span::hygiene::{ExpnHash, ExpnId};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -217,6 +218,7 @@ pub type MetadataLoaderDyn = dyn MetadataLoader + Sync;
|
|||
/// during resolve)
|
||||
pub trait CrateStore: std::fmt::Debug {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn untracked_as_any(&mut self) -> &mut dyn Any;
|
||||
|
||||
// Foreign definitions.
|
||||
// This information is safe to access, since it's hashed as part of the DefPathHash, which incr.
|
||||
|
|
@ -249,3 +251,11 @@ pub trait CrateStore: std::fmt::Debug {
|
|||
}
|
||||
|
||||
pub type CrateStoreDyn = dyn CrateStore + sync::Sync;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Untracked {
|
||||
pub cstore: Box<CrateStoreDyn>,
|
||||
/// Reference span for definitions.
|
||||
pub source_span: IndexVec<LocalDefId, Span>,
|
||||
pub definitions: RwLock<Definitions>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -368,7 +368,7 @@ mod desc {
|
|||
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
|
||||
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
|
||||
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
|
||||
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
|
||||
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
|
||||
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
|
||||
pub const parse_cfguard: &str =
|
||||
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
|
||||
|
|
@ -675,6 +675,7 @@ mod parse {
|
|||
*slot |= match s {
|
||||
"address" => SanitizerSet::ADDRESS,
|
||||
"cfi" => SanitizerSet::CFI,
|
||||
"kcfi" => SanitizerSet::KCFI,
|
||||
"leak" => SanitizerSet::LEAK,
|
||||
"memory" => SanitizerSet::MEMORY,
|
||||
"memtag" => SanitizerSet::MEMTAG,
|
||||
|
|
|
|||
|
|
@ -686,6 +686,10 @@ impl Session {
|
|||
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
|
||||
}
|
||||
|
||||
pub fn is_sanitizer_kcfi_enabled(&self) -> bool {
|
||||
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI)
|
||||
}
|
||||
|
||||
/// Check whether this compile session and crate type use static crt.
|
||||
pub fn crt_static(&self, crate_type: Option<CrateType>) -> bool {
|
||||
if !self.target.crt_static_respected {
|
||||
|
|
@ -1544,6 +1548,14 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
|
|||
}
|
||||
}
|
||||
|
||||
// LLVM CFI and KCFI are mutually exclusive
|
||||
if sess.is_sanitizer_cfi_enabled() && sess.is_sanitizer_kcfi_enabled() {
|
||||
sess.emit_err(CannotMixAndMatchSanitizers {
|
||||
first: "cfi".to_string(),
|
||||
second: "kcfi".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if sess.opts.unstable_opts.stack_protector != StackProtector::None {
|
||||
if !sess.target.options.supports_stack_protector {
|
||||
sess.emit_warning(StackProtectorNotSupportedForTarget {
|
||||
|
|
|
|||
|
|
@ -491,6 +491,10 @@ impl SpanData {
|
|||
pub fn is_dummy(self) -> bool {
|
||||
self.lo.0 == 0 && self.hi.0 == 0
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_visible(self, sm: &SourceMap) -> bool {
|
||||
!self.is_dummy() && sm.is_span_accessible(self.span())
|
||||
}
|
||||
/// Returns `true` if `self` fully encloses `other`.
|
||||
pub fn contains(self, other: Self) -> bool {
|
||||
self.lo <= other.lo && other.hi <= self.hi
|
||||
|
|
@ -556,6 +560,11 @@ impl Span {
|
|||
self.data_untracked().is_dummy()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_visible(self, sm: &SourceMap) -> bool {
|
||||
self.data_untracked().is_visible(sm)
|
||||
}
|
||||
|
||||
/// Returns `true` if this span comes from any kind of macro, desugaring or inlining.
|
||||
#[inline]
|
||||
pub fn from_expansion(self) -> bool {
|
||||
|
|
|
|||
|
|
@ -828,6 +828,7 @@ symbols! {
|
|||
item_like_imports,
|
||||
iter,
|
||||
iter_repeat,
|
||||
kcfi,
|
||||
keyword,
|
||||
kind,
|
||||
kreg,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ bitflags = "1.2.1"
|
|||
tracing = "0.1"
|
||||
punycode = "0.4.0"
|
||||
rustc-demangle = "0.1.21"
|
||||
twox-hash = "1.6.3"
|
||||
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
use rustc_middle::ty::{FnSig, Ty, TyCtxt};
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use std::hash::Hasher;
|
||||
use twox_hash::XxHash64;
|
||||
|
||||
mod typeid_itanium_cxx_abi;
|
||||
use typeid_itanium_cxx_abi::TypeIdOptions;
|
||||
|
|
@ -16,3 +18,25 @@ pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>)
|
|||
pub fn typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> String {
|
||||
typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS)
|
||||
}
|
||||
|
||||
/// Returns an LLVM KCFI type metadata identifier for the specified FnAbi.
|
||||
pub fn kcfi_typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> u32 {
|
||||
// An LLVM KCFI type metadata identifier is a 32-bit constant produced by taking the lower half
|
||||
// of the xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
|
||||
let mut hash: XxHash64 = Default::default();
|
||||
hash.write(
|
||||
typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, TypeIdOptions::NO_OPTIONS).as_bytes(),
|
||||
);
|
||||
hash.finish() as u32
|
||||
}
|
||||
|
||||
/// Returns an LLVM KCFI type metadata identifier for the specified FnSig.
|
||||
pub fn kcfi_typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> u32 {
|
||||
// An LLVM KCFI type metadata identifier is a 32-bit constant produced by taking the lower half
|
||||
// of the xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
|
||||
let mut hash: XxHash64 = Default::default();
|
||||
hash.write(
|
||||
typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS).as_bytes(),
|
||||
);
|
||||
hash.finish() as u32
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,16 @@
|
|||
//
|
||||
// For example, `-C target-cpu=cortex-a53`.
|
||||
|
||||
use super::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions};
|
||||
use super::{
|
||||
Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, Target, TargetOptions,
|
||||
};
|
||||
|
||||
pub fn target() -> Target {
|
||||
let opts = TargetOptions {
|
||||
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
|
||||
linker: Some("rust-lld".into()),
|
||||
features: "+strict-align,+neon,+fp-armv8".into(),
|
||||
supported_sanitizers: SanitizerSet::KCFI,
|
||||
relocation_model: RelocModel::Static,
|
||||
disable_redzone: true,
|
||||
max_atomic_width: Some(128),
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue