commit
ac667ef303
313 changed files with 2693 additions and 1525 deletions
|
|
@ -2814,9 +2814,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.23"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205"
|
||||
checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -503,7 +503,7 @@ Compatibility Notes
|
|||
* We have renamed `std::panic::PanicInfo` to `std::panic::PanicHookInfo`. The old name will continue to work as an alias, but will result in a deprecation warning starting in Rust 1.82.0.
|
||||
|
||||
`core::panic::PanicInfo` will remain unchanged, however, as this is now a *different type*.
|
||||
|
||||
|
||||
The reason is that these types have different roles: `std::panic::PanicHookInfo` is the argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in std context (where panics can have an arbitrary payload), while `core::panic::PanicInfo` is the argument to the [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) in no_std context (where panics always carry a formatted *message*). Separating these types allows us to add more useful methods to these types, such as `std::panic::PanicHookInfo::payload_as_str()` and `core::panic::PanicInfo::message()`.
|
||||
|
||||
* The new sort implementations may panic if a type's implementation of [`Ord`](https://doc.rust-lang.org/std/cmp/trait.Ord.html) (or the given comparison function) does not implement a [total order](https://en.wikipedia.org/wiki/Total_order) as the trait requires. `Ord`'s supertraits (`PartialOrd`, `Eq`, and `PartialEq`) must also be consistent. The previous implementations would not "notice" any problem, but the new implementations have a good chance of detecting inconsistencies, throwing a panic rather than returning knowingly unsorted data.
|
||||
|
|
@ -584,7 +584,7 @@ Stabilized APIs
|
|||
- [`impl Default for Arc<CStr>`](https://doc.rust-lang.org/beta/alloc/sync/struct.Arc.html#impl-Default-for-Arc%3CCStr%3E)
|
||||
- [`impl Default for Arc<[T]>`](https://doc.rust-lang.org/beta/alloc/sync/struct.Arc.html#impl-Default-for-Arc%3C%5BT%5D%3E)
|
||||
- [`impl IntoIterator for Box<[T]>`](https://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#impl-IntoIterator-for-Box%3C%5BI%5D,+A%3E)
|
||||
- [`impl FromIterator<String> for Box<str>`](https://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#impl-FromIterator%3CString%3E-for-Box%3Cstr%3E)
|
||||
- [`impl FromIterator<String> for Box<str>`](https://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#impl-FromIterator%3CString%3E-for-Box%3Cstr%3E)
|
||||
- [`impl FromIterator<char> for Box<str>`](https://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#impl-FromIterator%3Cchar%3E-for-Box%3Cstr%3E)
|
||||
- [`LazyCell`](https://doc.rust-lang.org/beta/core/cell/struct.LazyCell.html)
|
||||
- [`LazyLock`](https://doc.rust-lang.org/beta/std/sync/struct.LazyLock.html)
|
||||
|
|
@ -1816,7 +1816,7 @@ Compiler
|
|||
- [Detect uninhabited types early in const eval](https://github.com/rust-lang/rust/pull/109435/)
|
||||
- [Switch to LLD as default linker for {arm,thumb}v4t-none-eabi](https://github.com/rust-lang/rust/pull/109721/)
|
||||
- [Add tier 3 target `loongarch64-unknown-linux-gnu`](https://github.com/rust-lang/rust/pull/96971)
|
||||
- [Add tier 3 target for `i586-pc-nto-qnx700` (QNX Neutrino RTOS, version 7.0)](https://github.com/rust-lang/rust/pull/109173/),
|
||||
- [Add tier 3 target for `i586-pc-nto-qnx700` (QNX Neutrino RTOS, version 7.0)](https://github.com/rust-lang/rust/pull/109173/),
|
||||
- [Insert alignment checks for pointer dereferences as debug assertions](https://github.com/rust-lang/rust/pull/98112)
|
||||
This catches undefined behavior at runtime, and may cause existing code to fail.
|
||||
|
||||
|
|
@ -2023,7 +2023,7 @@ Compatibility Notes
|
|||
If `tools = [...]` is set in config.toml, we will respect a missing rustdoc in that list. By
|
||||
default rustdoc remains included. To retain the prior behavior explicitly add `"rustdoc"` to the
|
||||
list.
|
||||
|
||||
|
||||
<a id="1.69.0-Internal-Changes"></a>
|
||||
|
||||
Internal Changes
|
||||
|
|
|
|||
|
|
@ -627,9 +627,11 @@ impl Pat {
|
|||
| PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),
|
||||
|
||||
// Trivial wrappers over inner patterns.
|
||||
PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => {
|
||||
s.walk(it)
|
||||
}
|
||||
PatKind::Box(s)
|
||||
| PatKind::Deref(s)
|
||||
| PatKind::Ref(s, _)
|
||||
| PatKind::Paren(s)
|
||||
| PatKind::Guard(s, _) => s.walk(it),
|
||||
|
||||
// These patterns do not contain subpatterns, skip.
|
||||
PatKind::Wild
|
||||
|
|
@ -839,6 +841,9 @@ pub enum PatKind {
|
|||
// A never pattern `!`.
|
||||
Never,
|
||||
|
||||
/// A guard pattern (e.g., `x if guard(x)`).
|
||||
Guard(P<Pat>, P<Expr>),
|
||||
|
||||
/// Parentheses in patterns used for grouping (i.e., `(PAT)`).
|
||||
Paren(P<Pat>),
|
||||
|
||||
|
|
|
|||
|
|
@ -1525,6 +1525,10 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) {
|
|||
visit_opt(e2, |e| vis.visit_expr(e));
|
||||
vis.visit_span(span);
|
||||
}
|
||||
PatKind::Guard(p, e) => {
|
||||
vis.visit_pat(p);
|
||||
vis.visit_expr(e);
|
||||
}
|
||||
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
|
||||
visit_thin_vec(elems, |elem| vis.visit_pat(elem))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -682,6 +682,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
|
|||
visit_opt!(visitor, visit_expr, lower_bound);
|
||||
visit_opt!(visitor, visit_expr, upper_bound);
|
||||
}
|
||||
PatKind::Guard(subpattern, guard_condition) => {
|
||||
try_visit!(visitor.visit_pat(subpattern));
|
||||
try_visit!(visitor.visit_expr(guard_condition));
|
||||
}
|
||||
PatKind::Wild | PatKind::Rest | PatKind::Never => {}
|
||||
PatKind::Err(_guar) => {}
|
||||
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
|
||||
|
|
|
|||
|
|
@ -114,6 +114,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.lower_range_end(end, e2.is_some()),
|
||||
);
|
||||
}
|
||||
// FIXME(guard_patterns): lower pattern guards to HIR
|
||||
PatKind::Guard(inner, _) => pattern = inner,
|
||||
PatKind::Slice(pats) => break self.lower_pat_slice(pats),
|
||||
PatKind::Rest => {
|
||||
// If we reach here the `..` pattern is not semantically allowed.
|
||||
|
|
|
|||
|
|
@ -556,6 +556,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
|
||||
gate_all!(explicit_tail_calls, "`become` expression is experimental");
|
||||
gate_all!(generic_const_items, "generic const items are experimental");
|
||||
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
|
||||
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
|
||||
gate_all!(postfix_match, "postfix match is experimental");
|
||||
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
|
||||
|
|
|
|||
|
|
@ -1709,6 +1709,14 @@ impl<'a> State<'a> {
|
|||
self.print_expr(e, FixupContext::default());
|
||||
}
|
||||
}
|
||||
PatKind::Guard(subpat, condition) => {
|
||||
self.popen();
|
||||
self.print_pat(subpat);
|
||||
self.space();
|
||||
self.word_space("if");
|
||||
self.print_expr(condition, FixupContext::default());
|
||||
self.pclose();
|
||||
}
|
||||
PatKind::Slice(elts) => {
|
||||
self.word("[");
|
||||
self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
|
||||
|
|
|
|||
|
|
@ -1450,6 +1450,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
ty::Param(param_ty) => Ok((
|
||||
generics.type_param(param_ty, tcx),
|
||||
predicate.trait_ref.print_trait_sugared().to_string(),
|
||||
Some(predicate.trait_ref.def_id),
|
||||
)),
|
||||
_ => Err(()),
|
||||
}
|
||||
|
|
@ -1463,9 +1464,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
tcx,
|
||||
hir_generics,
|
||||
err,
|
||||
predicates
|
||||
.iter()
|
||||
.map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
|
||||
predicates.iter().map(|(param, constraint, def_id)| {
|
||||
(param.name.as_str(), &**constraint, *def_id)
|
||||
}),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,8 +227,6 @@ impl CodegenBackend for CraneliftCodegenBackend {
|
|||
sess: &Session,
|
||||
outputs: &OutputFilenames,
|
||||
) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
|
||||
let _timer = sess.timer("finish_ongoing_codegen");
|
||||
|
||||
ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join(sess, outputs)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -352,84 +352,84 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
| sym::saturating_add
|
||||
| sym::saturating_sub => {
|
||||
let ty = arg_tys[0];
|
||||
match int_type_width_signed(ty, self) {
|
||||
Some((width, signed)) => match name {
|
||||
sym::ctlz | sym::cttz => {
|
||||
let y = self.const_bool(false);
|
||||
let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[
|
||||
args[0].immediate(),
|
||||
y,
|
||||
]);
|
||||
if !ty.is_integral() {
|
||||
tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
|
||||
span,
|
||||
name,
|
||||
ty,
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
let (size, signed) = ty.int_size_and_signed(self.tcx);
|
||||
let width = size.bits();
|
||||
match name {
|
||||
sym::ctlz | sym::cttz => {
|
||||
let y = self.const_bool(false);
|
||||
let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[
|
||||
args[0].immediate(),
|
||||
y,
|
||||
]);
|
||||
|
||||
self.intcast(ret, llret_ty, false)
|
||||
}
|
||||
sym::ctlz_nonzero => {
|
||||
let y = self.const_bool(true);
|
||||
let llvm_name = &format!("llvm.ctlz.i{width}");
|
||||
let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
|
||||
self.intcast(ret, llret_ty, false)
|
||||
}
|
||||
sym::cttz_nonzero => {
|
||||
let y = self.const_bool(true);
|
||||
let llvm_name = &format!("llvm.cttz.i{width}");
|
||||
let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
|
||||
self.intcast(ret, llret_ty, false)
|
||||
}
|
||||
sym::ctpop => {
|
||||
let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[args
|
||||
[0]
|
||||
.immediate()]);
|
||||
self.intcast(ret, llret_ty, false)
|
||||
}
|
||||
sym::bswap => {
|
||||
if width == 8 {
|
||||
args[0].immediate() // byte swap a u8/i8 is just a no-op
|
||||
} else {
|
||||
self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[
|
||||
args[0].immediate()
|
||||
])
|
||||
}
|
||||
}
|
||||
sym::bitreverse => self
|
||||
.call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[
|
||||
args[0].immediate()
|
||||
]),
|
||||
sym::rotate_left | sym::rotate_right => {
|
||||
let is_left = name == sym::rotate_left;
|
||||
let val = args[0].immediate();
|
||||
let raw_shift = args[1].immediate();
|
||||
// rotate = funnel shift with first two args the same
|
||||
let llvm_name =
|
||||
&format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width);
|
||||
|
||||
// llvm expects shift to be the same type as the values, but rust
|
||||
// always uses `u32`.
|
||||
let raw_shift = self.intcast(raw_shift, self.val_ty(val), false);
|
||||
|
||||
self.call_intrinsic(llvm_name, &[val, val, raw_shift])
|
||||
}
|
||||
sym::saturating_add | sym::saturating_sub => {
|
||||
let is_add = name == sym::saturating_add;
|
||||
let lhs = args[0].immediate();
|
||||
let rhs = args[1].immediate();
|
||||
let llvm_name = &format!(
|
||||
"llvm.{}{}.sat.i{}",
|
||||
if signed { 's' } else { 'u' },
|
||||
if is_add { "add" } else { "sub" },
|
||||
width
|
||||
);
|
||||
self.call_intrinsic(llvm_name, &[lhs, rhs])
|
||||
}
|
||||
_ => bug!(),
|
||||
},
|
||||
None => {
|
||||
tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
|
||||
span,
|
||||
name,
|
||||
ty,
|
||||
});
|
||||
return Ok(());
|
||||
self.intcast(ret, llret_ty, false)
|
||||
}
|
||||
sym::ctlz_nonzero => {
|
||||
let y = self.const_bool(true);
|
||||
let llvm_name = &format!("llvm.ctlz.i{width}");
|
||||
let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
|
||||
self.intcast(ret, llret_ty, false)
|
||||
}
|
||||
sym::cttz_nonzero => {
|
||||
let y = self.const_bool(true);
|
||||
let llvm_name = &format!("llvm.cttz.i{width}");
|
||||
let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
|
||||
self.intcast(ret, llret_ty, false)
|
||||
}
|
||||
sym::ctpop => {
|
||||
let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[
|
||||
args[0].immediate()
|
||||
]);
|
||||
self.intcast(ret, llret_ty, false)
|
||||
}
|
||||
sym::bswap => {
|
||||
if width == 8 {
|
||||
args[0].immediate() // byte swap a u8/i8 is just a no-op
|
||||
} else {
|
||||
self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[
|
||||
args[0].immediate()
|
||||
])
|
||||
}
|
||||
}
|
||||
sym::bitreverse => self
|
||||
.call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[
|
||||
args[0].immediate()
|
||||
]),
|
||||
sym::rotate_left | sym::rotate_right => {
|
||||
let is_left = name == sym::rotate_left;
|
||||
let val = args[0].immediate();
|
||||
let raw_shift = args[1].immediate();
|
||||
// rotate = funnel shift with first two args the same
|
||||
let llvm_name =
|
||||
&format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width);
|
||||
|
||||
// llvm expects shift to be the same type as the values, but rust
|
||||
// always uses `u32`.
|
||||
let raw_shift = self.intcast(raw_shift, self.val_ty(val), false);
|
||||
|
||||
self.call_intrinsic(llvm_name, &[val, val, raw_shift])
|
||||
}
|
||||
sym::saturating_add | sym::saturating_sub => {
|
||||
let is_add = name == sym::saturating_add;
|
||||
let lhs = args[0].immediate();
|
||||
let rhs = args[1].immediate();
|
||||
let llvm_name = &format!(
|
||||
"llvm.{}{}.sat.i{}",
|
||||
if signed { 's' } else { 'u' },
|
||||
if is_add { "add" } else { "sub" },
|
||||
width
|
||||
);
|
||||
self.call_intrinsic(llvm_name, &[lhs, rhs])
|
||||
}
|
||||
_ => bug!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2531,19 +2531,3 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
|||
|
||||
span_bug!(span, "unknown SIMD intrinsic");
|
||||
}
|
||||
|
||||
// Returns the width of an int Ty, and if it's signed or not
|
||||
// Returns None if the type is not an integer
|
||||
// FIXME: there’s multiple of this functions, investigate using some of the already existing
|
||||
// stuffs.
|
||||
fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> {
|
||||
match ty.kind() {
|
||||
ty::Int(t) => {
|
||||
Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), true))
|
||||
}
|
||||
ty::Uint(t) => {
|
||||
Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), false))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ use rustc_codegen_ssa::back::write::{
|
|||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError};
|
||||
use rustc_errors::{DiagCtxtHandle, FatalError};
|
||||
use rustc_metadata::EncodedMetadata;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
|
@ -370,19 +370,14 @@ impl CodegenBackend for LlvmCodegenBackend {
|
|||
(codegen_results, work_products)
|
||||
}
|
||||
|
||||
fn link(
|
||||
&self,
|
||||
sess: &Session,
|
||||
codegen_results: CodegenResults,
|
||||
outputs: &OutputFilenames,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) {
|
||||
use rustc_codegen_ssa::back::link::link_binary;
|
||||
|
||||
use crate::back::archive::LlvmArchiveBuilderBuilder;
|
||||
|
||||
// Run the linker on any artifacts that resulted from the LLVM run.
|
||||
// This should produce either a finished executable or library.
|
||||
link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, outputs)
|
||||
link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, outputs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use rustc_ast::CRATE_NODE_ID;
|
|||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError};
|
||||
use rustc_errors::{DiagCtxtHandle, FatalError};
|
||||
use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
|
||||
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
|
||||
use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
|
||||
|
|
@ -71,7 +71,7 @@ pub fn link_binary(
|
|||
archive_builder_builder: &dyn ArchiveBuilderBuilder,
|
||||
codegen_results: CodegenResults,
|
||||
outputs: &OutputFilenames,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
) {
|
||||
let _timer = sess.timer("link_binary");
|
||||
let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
|
||||
let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new();
|
||||
|
|
@ -119,7 +119,7 @@ pub fn link_binary(
|
|||
&codegen_results,
|
||||
RlibFlavor::Normal,
|
||||
&path,
|
||||
)?
|
||||
)
|
||||
.build(&out_filename);
|
||||
}
|
||||
CrateType::Staticlib => {
|
||||
|
|
@ -129,7 +129,7 @@ pub fn link_binary(
|
|||
&codegen_results,
|
||||
&out_filename,
|
||||
&path,
|
||||
)?;
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
link_natively(
|
||||
|
|
@ -139,7 +139,7 @@ pub fn link_binary(
|
|||
&out_filename,
|
||||
&codegen_results,
|
||||
path.as_ref(),
|
||||
)?;
|
||||
);
|
||||
}
|
||||
}
|
||||
if sess.opts.json_artifact_notifications {
|
||||
|
|
@ -225,8 +225,6 @@ pub fn link_binary(
|
|||
maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Crate type is not passed when calculating the dylibs to include for LTO. In that case all
|
||||
|
|
@ -298,7 +296,7 @@ fn link_rlib<'a>(
|
|||
codegen_results: &CodegenResults,
|
||||
flavor: RlibFlavor,
|
||||
tmpdir: &MaybeTempDir,
|
||||
) -> Result<Box<dyn ArchiveBuilder + 'a>, ErrorGuaranteed> {
|
||||
) -> Box<dyn ArchiveBuilder + 'a> {
|
||||
let mut ab = archive_builder_builder.new_archive_builder(sess);
|
||||
|
||||
let trailing_metadata = match flavor {
|
||||
|
|
@ -374,7 +372,7 @@ fn link_rlib<'a>(
|
|||
{
|
||||
let path = find_native_static_library(filename.as_str(), true, sess);
|
||||
let src = read(path)
|
||||
.map_err(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }))?;
|
||||
.unwrap_or_else(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }));
|
||||
let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src);
|
||||
let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str());
|
||||
packed_bundled_libs.push(wrapper_file);
|
||||
|
|
@ -392,7 +390,7 @@ fn link_rlib<'a>(
|
|||
codegen_results.crate_info.used_libraries.iter(),
|
||||
tmpdir.as_ref(),
|
||||
true,
|
||||
)? {
|
||||
) {
|
||||
ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
|
||||
sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
|
||||
});
|
||||
|
|
@ -433,7 +431,7 @@ fn link_rlib<'a>(
|
|||
ab.add_file(&lib)
|
||||
}
|
||||
|
||||
Ok(ab)
|
||||
ab
|
||||
}
|
||||
|
||||
/// Extract all symbols defined in raw-dylib libraries, collated by library name.
|
||||
|
|
@ -445,7 +443,7 @@ fn link_rlib<'a>(
|
|||
fn collate_raw_dylibs<'a>(
|
||||
sess: &Session,
|
||||
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
|
||||
) -> Result<Vec<(String, Vec<DllImport>)>, ErrorGuaranteed> {
|
||||
) -> Vec<(String, Vec<DllImport>)> {
|
||||
// Use index maps to preserve original order of imports and libraries.
|
||||
let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default();
|
||||
|
||||
|
|
@ -469,15 +467,13 @@ fn collate_raw_dylibs<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
if let Some(guar) = sess.dcx().has_errors() {
|
||||
return Err(guar);
|
||||
}
|
||||
Ok(dylib_table
|
||||
sess.dcx().abort_if_errors();
|
||||
dylib_table
|
||||
.into_iter()
|
||||
.map(|(name, imports)| {
|
||||
(name, imports.into_iter().map(|(_, import)| import.clone()).collect())
|
||||
})
|
||||
.collect())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn create_dll_import_libs<'a>(
|
||||
|
|
@ -486,8 +482,8 @@ fn create_dll_import_libs<'a>(
|
|||
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
|
||||
tmpdir: &Path,
|
||||
is_direct_dependency: bool,
|
||||
) -> Result<Vec<PathBuf>, ErrorGuaranteed> {
|
||||
Ok(collate_raw_dylibs(sess, used_libraries)?
|
||||
) -> Vec<PathBuf> {
|
||||
collate_raw_dylibs(sess, used_libraries)
|
||||
.into_iter()
|
||||
.map(|(raw_dylib_name, raw_dylib_imports)| {
|
||||
let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
|
||||
|
|
@ -537,7 +533,7 @@ fn create_dll_import_libs<'a>(
|
|||
|
||||
output_path
|
||||
})
|
||||
.collect())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Create a static archive.
|
||||
|
|
@ -557,7 +553,7 @@ fn link_staticlib(
|
|||
codegen_results: &CodegenResults,
|
||||
out_filename: &Path,
|
||||
tempdir: &MaybeTempDir,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
) {
|
||||
info!("preparing staticlib to {:?}", out_filename);
|
||||
let mut ab = link_rlib(
|
||||
sess,
|
||||
|
|
@ -565,7 +561,7 @@ fn link_staticlib(
|
|||
codegen_results,
|
||||
RlibFlavor::StaticlibBase,
|
||||
tempdir,
|
||||
)?;
|
||||
);
|
||||
let mut all_native_libs = vec![];
|
||||
|
||||
let res = each_linked_rlib(
|
||||
|
|
@ -656,8 +652,6 @@ fn link_staticlib(
|
|||
print_native_static_libs(sess, &print.out, &all_native_libs, &all_rust_dylibs);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Use `thorin` (rust implementation of a dwarf packaging utility) to link DWARF objects into a
|
||||
|
|
@ -773,7 +767,7 @@ fn link_natively(
|
|||
out_filename: &Path,
|
||||
codegen_results: &CodegenResults,
|
||||
tmpdir: &Path,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
) {
|
||||
info!("preparing {:?} to {:?}", crate_type, out_filename);
|
||||
let (linker_path, flavor) = linker_and_flavor(sess);
|
||||
let self_contained_components = self_contained_components(sess, crate_type);
|
||||
|
|
@ -797,7 +791,7 @@ fn link_natively(
|
|||
temp_filename,
|
||||
codegen_results,
|
||||
self_contained_components,
|
||||
)?;
|
||||
);
|
||||
|
||||
linker::disable_localization(&mut cmd);
|
||||
|
||||
|
|
@ -1177,8 +1171,6 @@ fn link_natively(
|
|||
ab.add_file(temp_filename);
|
||||
ab.build(out_filename);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn strip_symbols_with_external_utility(
|
||||
|
|
@ -2232,7 +2224,7 @@ fn linker_with_args(
|
|||
out_filename: &Path,
|
||||
codegen_results: &CodegenResults,
|
||||
self_contained_components: LinkSelfContainedComponents,
|
||||
) -> Result<Command, ErrorGuaranteed> {
|
||||
) -> Command {
|
||||
let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled();
|
||||
let cmd = &mut *super::linker::get_linker(
|
||||
sess,
|
||||
|
|
@ -2356,7 +2348,7 @@ fn linker_with_args(
|
|||
codegen_results.crate_info.used_libraries.iter(),
|
||||
tmpdir,
|
||||
true,
|
||||
)? {
|
||||
) {
|
||||
cmd.add_object(&output_path);
|
||||
}
|
||||
// As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case
|
||||
|
|
@ -2388,7 +2380,7 @@ fn linker_with_args(
|
|||
native_libraries_from_nonstatics,
|
||||
tmpdir,
|
||||
false,
|
||||
)? {
|
||||
) {
|
||||
cmd.add_object(&output_path);
|
||||
}
|
||||
|
||||
|
|
@ -2435,7 +2427,7 @@ fn linker_with_args(
|
|||
// to it and remove the option. Currently the last holdout is wasm32-unknown-emscripten.
|
||||
add_post_link_args(cmd, sess, flavor);
|
||||
|
||||
Ok(cmd.take_cmd())
|
||||
cmd.take_cmd()
|
||||
}
|
||||
|
||||
fn add_order_independent_options(
|
||||
|
|
|
|||
|
|
@ -1883,7 +1883,11 @@ impl Translate for SharedEmitter {
|
|||
}
|
||||
|
||||
impl Emitter for SharedEmitter {
|
||||
fn emit_diagnostic(&mut self, mut diag: rustc_errors::DiagInner) {
|
||||
fn emit_diagnostic(
|
||||
&mut self,
|
||||
mut diag: rustc_errors::DiagInner,
|
||||
_registry: &rustc_errors::registry::Registry,
|
||||
) {
|
||||
// Check that we aren't missing anything interesting when converting to
|
||||
// the cut-down local `DiagInner`.
|
||||
assert_eq!(diag.span, MultiSpan::new());
|
||||
|
|
@ -2028,8 +2032,6 @@ pub struct OngoingCodegen<B: ExtraBackendMethods> {
|
|||
|
||||
impl<B: ExtraBackendMethods> OngoingCodegen<B> {
|
||||
pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
|
||||
let _timer = sess.timer("finish_ongoing_codegen");
|
||||
|
||||
self.shared_emitter_main.check(sess, true);
|
||||
let compiled_modules = sess.time("join_worker_thread", || match self.coordinator.join() {
|
||||
Ok(Ok(compiled_modules)) => compiled_modules,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use std::hash::Hash;
|
|||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::sync::{DynSend, DynSync};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_metadata::EncodedMetadata;
|
||||
use rustc_metadata::creader::MetadataLoaderDyn;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
|
|
@ -84,13 +83,8 @@ pub trait CodegenBackend {
|
|||
) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>);
|
||||
|
||||
/// This is called on the returned [`CodegenResults`] from [`join_codegen`](Self::join_codegen).
|
||||
fn link(
|
||||
&self,
|
||||
sess: &Session,
|
||||
codegen_results: CodegenResults,
|
||||
outputs: &OutputFilenames,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs)
|
||||
fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) {
|
||||
link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs);
|
||||
}
|
||||
|
||||
/// Returns `true` if this backend can be safely called from multiple threads.
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
|||
err,
|
||||
param_ty.name.as_str(),
|
||||
&constraint,
|
||||
None,
|
||||
Some(trait_ref.def_id),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1018,29 +1018,48 @@ where
|
|||
self.allocate_dyn(layout, kind, MemPlaceMeta::None)
|
||||
}
|
||||
|
||||
/// Returns a wide MPlace of type `str` to a new 1-aligned allocation.
|
||||
/// Immutable strings are deduplicated and stored in global memory.
|
||||
/// Allocates a sequence of bytes in the interpreter's memory.
|
||||
/// For immutable allocations, uses deduplication to reuse existing memory.
|
||||
/// For mutable allocations, creates a new unique allocation.
|
||||
pub fn allocate_bytes(
|
||||
&mut self,
|
||||
bytes: &[u8],
|
||||
align: Align,
|
||||
kind: MemoryKind<M::MemoryKind>,
|
||||
mutbl: Mutability,
|
||||
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
|
||||
// Use cache for immutable strings.
|
||||
if mutbl.is_not() {
|
||||
// Use dedup'd allocation function.
|
||||
let salt = M::get_global_alloc_salt(self, None);
|
||||
let id = self.tcx.allocate_bytes_dedup(bytes, salt);
|
||||
|
||||
// Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation.
|
||||
M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))
|
||||
} else {
|
||||
// Allocate new memory for mutable data.
|
||||
self.allocate_bytes_ptr(bytes, align, kind, mutbl)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a string in the interpreter's memory with metadata for length.
|
||||
/// Uses `allocate_bytes` internally but adds string-specific metadata handling.
|
||||
pub fn allocate_str(
|
||||
&mut self,
|
||||
str: &str,
|
||||
kind: MemoryKind<M::MemoryKind>,
|
||||
mutbl: Mutability,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
let tcx = self.tcx.tcx;
|
||||
let bytes = str.as_bytes();
|
||||
let ptr = self.allocate_bytes(bytes, Align::ONE, kind, mutbl)?;
|
||||
|
||||
// Use cache for immutable strings.
|
||||
let ptr = if mutbl.is_not() {
|
||||
// Use dedup'd allocation function.
|
||||
let salt = M::get_global_alloc_salt(self, None);
|
||||
let id = tcx.allocate_bytes_dedup(str.as_bytes(), salt);
|
||||
// Create length metadata for the string.
|
||||
let meta = Scalar::from_target_usize(u64::try_from(bytes.len()).unwrap(), self);
|
||||
|
||||
// Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation.
|
||||
M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))?
|
||||
} else {
|
||||
self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)?
|
||||
};
|
||||
let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
|
||||
// Get layout for Rust's str type.
|
||||
let layout = self.layout_of(self.tcx.types.str_).unwrap();
|
||||
|
||||
// Combine pointer and metadata into a wide pointer.
|
||||
interp_ok(self.ptr_with_meta_to_mplace(
|
||||
ptr.into(),
|
||||
MemPlaceMeta::Meta(meta),
|
||||
|
|
|
|||
|
|
@ -99,10 +99,7 @@ impl Expander {
|
|||
/// If this function is intended to be used with command line arguments,
|
||||
/// `argv[0]` must be removed prior to calling it manually.
|
||||
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
|
||||
pub fn arg_expand_all(
|
||||
early_dcx: &EarlyDiagCtxt,
|
||||
at_args: &[String],
|
||||
) -> Result<Vec<String>, ErrorGuaranteed> {
|
||||
pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<String> {
|
||||
let mut expander = Expander::default();
|
||||
let mut result = Ok(());
|
||||
for arg in at_args {
|
||||
|
|
@ -110,7 +107,10 @@ pub fn arg_expand_all(
|
|||
result = Err(early_dcx.early_err(format!("failed to load argument file: {err}")));
|
||||
}
|
||||
}
|
||||
result.map(|()| expander.finish())
|
||||
if let Err(guar) = result {
|
||||
guar.raise_fatal();
|
||||
}
|
||||
expander.finish()
|
||||
}
|
||||
|
||||
/// Gets the raw unprocessed command-line arguments as Unicode strings, without doing any further
|
||||
|
|
|
|||
|
|
@ -42,9 +42,7 @@ use rustc_data_structures::profiling::{
|
|||
};
|
||||
use rustc_errors::emitter::stderr_destination;
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::{
|
||||
ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult, markdown,
|
||||
};
|
||||
use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown};
|
||||
use rustc_feature::find_gated_cfg;
|
||||
use rustc_interface::util::{self, get_codegen_backend};
|
||||
use rustc_interface::{Linker, Queries, interface, passes};
|
||||
|
|
@ -271,14 +269,14 @@ impl<'a> RunCompiler<'a> {
|
|||
}
|
||||
|
||||
/// Parse args and run the compiler.
|
||||
pub fn run(self) -> interface::Result<()> {
|
||||
pub fn run(self) {
|
||||
run_compiler(
|
||||
self.at_args,
|
||||
self.callbacks,
|
||||
self.file_loader,
|
||||
self.make_codegen_backend,
|
||||
self.using_internal_features,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -290,7 +288,7 @@ fn run_compiler(
|
|||
Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
|
||||
>,
|
||||
using_internal_features: Arc<std::sync::atomic::AtomicBool>,
|
||||
) -> interface::Result<()> {
|
||||
) {
|
||||
let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
|
||||
|
||||
// Throw away the first argument, the name of the binary.
|
||||
|
|
@ -303,9 +301,11 @@ fn run_compiler(
|
|||
// the compiler with @empty_file as argv[0] and no more arguments.
|
||||
let at_args = at_args.get(1..).unwrap_or_default();
|
||||
|
||||
let args = args::arg_expand_all(&default_early_dcx, at_args)?;
|
||||
let args = args::arg_expand_all(&default_early_dcx, at_args);
|
||||
|
||||
let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) };
|
||||
let Some(matches) = handle_options(&default_early_dcx, &args) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let sopts = config::build_session_options(&mut default_early_dcx, &matches);
|
||||
// fully initialize ice path static once unstable options are available as context
|
||||
|
|
@ -313,7 +313,7 @@ fn run_compiler(
|
|||
|
||||
if let Some(ref code) = matches.opt_str("explain") {
|
||||
handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color);
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
|
||||
let (odir, ofile) = make_output(&matches);
|
||||
|
|
@ -338,7 +338,7 @@ fn run_compiler(
|
|||
expanded_args: args,
|
||||
};
|
||||
|
||||
let has_input = match make_input(&default_early_dcx, &matches.free)? {
|
||||
let has_input = match make_input(&default_early_dcx, &matches.free) {
|
||||
Some(input) => {
|
||||
config.input = input;
|
||||
true // has input: normal compilation
|
||||
|
|
@ -358,7 +358,7 @@ fn run_compiler(
|
|||
// printing some information without compiling, or exiting immediately
|
||||
// after parsing, etc.
|
||||
let early_exit = || {
|
||||
if let Some(guar) = sess.dcx().has_errors() { Err(guar) } else { Ok(()) }
|
||||
sess.dcx().abort_if_errors();
|
||||
};
|
||||
|
||||
// This implements `-Whelp`. It should be handled very early, like
|
||||
|
|
@ -389,22 +389,25 @@ fn run_compiler(
|
|||
}
|
||||
|
||||
let linker = compiler.enter(|queries| {
|
||||
let early_exit = || early_exit().map(|_| None);
|
||||
let early_exit = || {
|
||||
sess.dcx().abort_if_errors();
|
||||
None
|
||||
};
|
||||
|
||||
// Parse the crate root source code (doesn't parse submodules yet)
|
||||
// Everything else is parsed during macro expansion.
|
||||
queries.parse()?;
|
||||
queries.parse();
|
||||
|
||||
// If pretty printing is requested: Figure out the representation, print it and exit
|
||||
if let Some(pp_mode) = sess.opts.pretty {
|
||||
if pp_mode.needs_ast_map() {
|
||||
queries.global_ctxt()?.enter(|tcx| {
|
||||
queries.global_ctxt().enter(|tcx| {
|
||||
tcx.ensure().early_lint_checks(());
|
||||
pretty::print(sess, pp_mode, pretty::PrintExtra::NeedsAstMap { tcx });
|
||||
passes::write_dep_info(tcx);
|
||||
});
|
||||
} else {
|
||||
let krate = queries.parse()?;
|
||||
let krate = queries.parse();
|
||||
pretty::print(sess, pp_mode, pretty::PrintExtra::AfterParsing {
|
||||
krate: &*krate.borrow(),
|
||||
});
|
||||
|
|
@ -423,17 +426,17 @@ fn run_compiler(
|
|||
}
|
||||
|
||||
// Make sure name resolution and macro expansion is run.
|
||||
queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering());
|
||||
queries.global_ctxt().enter(|tcx| tcx.resolver_for_lowering());
|
||||
|
||||
if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
|
||||
queries.global_ctxt()?.enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir));
|
||||
queries.global_ctxt().enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir));
|
||||
}
|
||||
|
||||
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
queries.global_ctxt()?.enter(|tcx| {
|
||||
queries.global_ctxt().enter(|tcx| {
|
||||
passes::write_dep_info(tcx);
|
||||
|
||||
if sess.opts.output_types.contains_key(&OutputType::DepInfo)
|
||||
|
|
@ -446,24 +449,21 @@ fn run_compiler(
|
|||
return early_exit();
|
||||
}
|
||||
|
||||
tcx.analysis(())?;
|
||||
tcx.ensure().analysis(());
|
||||
|
||||
if callbacks.after_analysis(compiler, tcx) == Compilation::Stop {
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
Ok(Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)?))
|
||||
Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend))
|
||||
})
|
||||
})?;
|
||||
});
|
||||
|
||||
// Linking is done outside the `compiler.enter()` so that the
|
||||
// `GlobalCtxt` within `Queries` can be freed as early as possible.
|
||||
if let Some(linker) = linker {
|
||||
let _timer = sess.timer("link");
|
||||
linker.link(sess, codegen_backend)?
|
||||
linker.link(sess, codegen_backend);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -496,21 +496,17 @@ fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileNa
|
|||
|
||||
/// Extract input (string or file and optional path) from matches.
|
||||
/// This handles reading from stdin if `-` is provided.
|
||||
fn make_input(
|
||||
early_dcx: &EarlyDiagCtxt,
|
||||
free_matches: &[String],
|
||||
) -> Result<Option<Input>, ErrorGuaranteed> {
|
||||
fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option<Input> {
|
||||
match free_matches {
|
||||
[] => Ok(None), // no input: we will exit early,
|
||||
[] => None, // no input: we will exit early,
|
||||
[ifile] if ifile == "-" => {
|
||||
// read from stdin as `Input::Str`
|
||||
let mut input = String::new();
|
||||
if io::stdin().read_to_string(&mut input).is_err() {
|
||||
// Immediately stop compilation if there was an issue reading
|
||||
// the input (for example if the input stream is not UTF-8).
|
||||
let reported = early_dcx
|
||||
.early_err("couldn't read from stdin, as it did not contain valid UTF-8");
|
||||
return Err(reported);
|
||||
early_dcx
|
||||
.early_fatal("couldn't read from stdin, as it did not contain valid UTF-8");
|
||||
}
|
||||
|
||||
let name = match env::var("UNSTABLE_RUSTDOC_TEST_PATH") {
|
||||
|
|
@ -526,9 +522,9 @@ fn make_input(
|
|||
Err(_) => FileName::anon_source_code(&input),
|
||||
};
|
||||
|
||||
Ok(Some(Input::Str { name, input }))
|
||||
Some(Input::Str { name, input })
|
||||
}
|
||||
[ifile] => Ok(Some(Input::File(PathBuf::from(ifile)))),
|
||||
[ifile] => Some(Input::File(PathBuf::from(ifile))),
|
||||
[ifile1, ifile2, ..] => early_dcx.early_fatal(format!(
|
||||
"multiple input filenames provided (first two filenames are `{}` and `{}`)",
|
||||
ifile1, ifile2
|
||||
|
|
@ -663,9 +659,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) {
|
|||
};
|
||||
}
|
||||
};
|
||||
if compiler.codegen_backend.link(sess, codegen_results, &outputs).is_err() {
|
||||
FatalError.raise();
|
||||
}
|
||||
compiler.codegen_backend.link(sess, codegen_results, &outputs);
|
||||
} else {
|
||||
dcx.emit_fatal(RlinkNotAFile {});
|
||||
}
|
||||
|
|
@ -1608,7 +1602,8 @@ pub fn main() -> ! {
|
|||
let exit_code = catch_with_exit_code(|| {
|
||||
RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks)
|
||||
.set_using_internal_features(using_internal_features)
|
||||
.run()
|
||||
.run();
|
||||
Ok(())
|
||||
});
|
||||
|
||||
if let Some(format) = callbacks.time_passes {
|
||||
|
|
|
|||
|
|
@ -222,8 +222,8 @@ impl<'tcx> PrintExtra<'tcx> {
|
|||
}
|
||||
|
||||
pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
|
||||
if ppm.needs_analysis() && ex.tcx().analysis(()).is_err() {
|
||||
FatalError.raise();
|
||||
if ppm.needs_analysis() {
|
||||
ex.tcx().ensure().analysis(());
|
||||
}
|
||||
|
||||
let (src, src_name) = get_source(sess);
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ impl !MyTrait for i32 { } // error!
|
|||
```
|
||||
|
||||
Negative implementations are a promise that the trait will never be implemented
|
||||
for the given types. Therefore, both cannot exists at the same time.
|
||||
for the given types. Therefore, both cannot exist at the same time.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use rustc_span::SourceFile;
|
|||
use rustc_span::source_map::SourceMap;
|
||||
|
||||
use crate::emitter::FileWithAnnotatedLines;
|
||||
use crate::registry::Registry;
|
||||
use crate::snippet::Line;
|
||||
use crate::translation::{Translate, to_fluent_args};
|
||||
use crate::{
|
||||
|
|
@ -45,7 +46,7 @@ impl Translate for AnnotateSnippetEmitter {
|
|||
|
||||
impl Emitter for AnnotateSnippetEmitter {
|
||||
/// The entry point for the diagnostics generation
|
||||
fn emit_diagnostic(&mut self, mut diag: DiagInner) {
|
||||
fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
|
||||
let fluent_args = to_fluent_args(diag.args.iter());
|
||||
|
||||
let mut suggestions = diag.suggestions.unwrap_tag();
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, StandardStr
|
|||
use tracing::{debug, instrument, trace, warn};
|
||||
|
||||
use crate::diagnostic::DiagLocation;
|
||||
use crate::registry::Registry;
|
||||
use crate::snippet::{
|
||||
Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString,
|
||||
};
|
||||
|
|
@ -181,7 +182,7 @@ pub type DynEmitter = dyn Emitter + DynSend;
|
|||
/// Emitter trait for emitting errors.
|
||||
pub trait Emitter: Translate {
|
||||
/// Emit a structured diagnostic.
|
||||
fn emit_diagnostic(&mut self, diag: DiagInner);
|
||||
fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry);
|
||||
|
||||
/// Emit a notification that an artifact has been output.
|
||||
/// Currently only supported for the JSON format.
|
||||
|
|
@ -189,7 +190,7 @@ pub trait Emitter: Translate {
|
|||
|
||||
/// Emit a report about future breakage.
|
||||
/// Currently only supported for the JSON format.
|
||||
fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>) {}
|
||||
fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>, _registry: &Registry) {}
|
||||
|
||||
/// Emit list of unused externs.
|
||||
/// Currently only supported for the JSON format.
|
||||
|
|
@ -500,7 +501,7 @@ impl Emitter for HumanEmitter {
|
|||
self.sm.as_deref()
|
||||
}
|
||||
|
||||
fn emit_diagnostic(&mut self, mut diag: DiagInner) {
|
||||
fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
|
||||
let fluent_args = to_fluent_args(diag.args.iter());
|
||||
|
||||
let mut suggestions = diag.suggestions.unwrap_tag();
|
||||
|
|
@ -561,7 +562,7 @@ impl Emitter for SilentEmitter {
|
|||
None
|
||||
}
|
||||
|
||||
fn emit_diagnostic(&mut self, mut diag: DiagInner) {
|
||||
fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
|
||||
if self.emit_fatal_diagnostic && diag.level == Level::Fatal {
|
||||
if let Some(fatal_note) = &self.fatal_note {
|
||||
diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new());
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ mod tests;
|
|||
pub struct JsonEmitter {
|
||||
#[setters(skip)]
|
||||
dst: IntoDynSyncSend<Box<dyn Write + Send>>,
|
||||
registry: Option<Registry>,
|
||||
#[setters(skip)]
|
||||
sm: Lrc<SourceMap>,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
|
|
@ -74,7 +73,6 @@ impl JsonEmitter {
|
|||
) -> JsonEmitter {
|
||||
JsonEmitter {
|
||||
dst: IntoDynSyncSend(dst),
|
||||
registry: None,
|
||||
sm,
|
||||
fluent_bundle: None,
|
||||
fallback_bundle,
|
||||
|
|
@ -121,8 +119,8 @@ impl Translate for JsonEmitter {
|
|||
}
|
||||
|
||||
impl Emitter for JsonEmitter {
|
||||
fn emit_diagnostic(&mut self, diag: crate::DiagInner) {
|
||||
let data = Diagnostic::from_errors_diagnostic(diag, self);
|
||||
fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) {
|
||||
let data = Diagnostic::from_errors_diagnostic(diag, self, registry);
|
||||
let result = self.emit(EmitTyped::Diagnostic(data));
|
||||
if let Err(e) = result {
|
||||
panic!("failed to print diagnostics: {e:?}");
|
||||
|
|
@ -137,7 +135,7 @@ impl Emitter for JsonEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>) {
|
||||
fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>, registry: &Registry) {
|
||||
let data: Vec<FutureBreakageItem<'_>> = diags
|
||||
.into_iter()
|
||||
.map(|mut diag| {
|
||||
|
|
@ -151,7 +149,7 @@ impl Emitter for JsonEmitter {
|
|||
}
|
||||
FutureBreakageItem {
|
||||
diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic(
|
||||
diag, self,
|
||||
diag, self, registry,
|
||||
)),
|
||||
}
|
||||
})
|
||||
|
|
@ -291,7 +289,11 @@ struct UnusedExterns<'a> {
|
|||
|
||||
impl Diagnostic {
|
||||
/// Converts from `rustc_errors::DiagInner` to `Diagnostic`.
|
||||
fn from_errors_diagnostic(diag: crate::DiagInner, je: &JsonEmitter) -> Diagnostic {
|
||||
fn from_errors_diagnostic(
|
||||
diag: crate::DiagInner,
|
||||
je: &JsonEmitter,
|
||||
registry: &Registry,
|
||||
) -> Diagnostic {
|
||||
let args = to_fluent_args(diag.args.iter());
|
||||
let sugg_to_diag = |sugg: &CodeSuggestion| {
|
||||
let translated_message =
|
||||
|
|
@ -344,7 +346,7 @@ impl Diagnostic {
|
|||
let code = if let Some(code) = diag.code {
|
||||
Some(DiagnosticCode {
|
||||
code: code.to_string(),
|
||||
explanation: je.registry.as_ref().unwrap().try_find_description(code).ok(),
|
||||
explanation: registry.try_find_description(code).ok(),
|
||||
})
|
||||
} else if let Some(IsLint { name, .. }) = &diag.is_lint {
|
||||
Some(DiagnosticCode { code: name.to_string(), explanation: None })
|
||||
|
|
@ -382,7 +384,7 @@ impl Diagnostic {
|
|||
} else {
|
||||
OutputTheme::Ascii
|
||||
})
|
||||
.emit_diagnostic(diag);
|
||||
.emit_diagnostic(diag, registry);
|
||||
let buf = Arc::try_unwrap(buf.0).unwrap().into_inner().unwrap();
|
||||
let buf = String::from_utf8(buf).unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ pub use diagnostic_impls::{
|
|||
};
|
||||
pub use emitter::ColorConfig;
|
||||
use emitter::{DynEmitter, Emitter, is_case_difference, is_different};
|
||||
use registry::Registry;
|
||||
use rustc_data_structures::AtomicRef;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::stable_hasher::{Hash128, StableHasher};
|
||||
|
|
@ -77,6 +76,8 @@ pub use snippet::Style;
|
|||
pub use termcolor::{Color, ColorSpec, WriteColor};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::registry::Registry;
|
||||
|
||||
pub mod annotate_snippet_emitter_writer;
|
||||
pub mod codes;
|
||||
mod diagnostic;
|
||||
|
|
@ -483,6 +484,8 @@ impl<'a> std::ops::Deref for DiagCtxtHandle<'a> {
|
|||
struct DiagCtxtInner {
|
||||
flags: DiagCtxtFlags,
|
||||
|
||||
registry: Registry,
|
||||
|
||||
/// The error guarantees from all emitted errors. The length gives the error count.
|
||||
err_guars: Vec<ErrorGuaranteed>,
|
||||
/// The error guarantee from all emitted lint errors. The length gives the
|
||||
|
|
@ -619,9 +622,7 @@ impl Drop for DiagCtxtInner {
|
|||
// Important: it is sound to produce an `ErrorGuaranteed` when emitting
|
||||
// delayed bugs because they are guaranteed to be emitted here if
|
||||
// necessary.
|
||||
if self.err_guars.is_empty() {
|
||||
self.flush_delayed()
|
||||
}
|
||||
self.flush_delayed();
|
||||
|
||||
// Sanity check: did we use some of the expensive `trimmed_def_paths` functions
|
||||
// unexpectedly, that is, without producing diagnostics? If so, for debugging purposes, we
|
||||
|
|
@ -664,6 +665,11 @@ impl DiagCtxt {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_registry(mut self, registry: Registry) -> Self {
|
||||
self.inner.get_mut().registry = registry;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn new(emitter: Box<DynEmitter>) -> Self {
|
||||
Self { inner: Lock::new(DiagCtxtInner::new(emitter)) }
|
||||
}
|
||||
|
|
@ -694,7 +700,7 @@ impl DiagCtxt {
|
|||
struct FalseEmitter;
|
||||
|
||||
impl Emitter for FalseEmitter {
|
||||
fn emit_diagnostic(&mut self, _: DiagInner) {
|
||||
fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) {
|
||||
unimplemented!("false emitter must only used during `wrap_emitter`")
|
||||
}
|
||||
|
||||
|
|
@ -759,6 +765,7 @@ impl DiagCtxt {
|
|||
let mut inner = self.inner.borrow_mut();
|
||||
let DiagCtxtInner {
|
||||
flags: _,
|
||||
registry: _,
|
||||
err_guars,
|
||||
lint_err_guars,
|
||||
delayed_bugs,
|
||||
|
|
@ -964,7 +971,7 @@ impl<'a> DiagCtxtHandle<'a> {
|
|||
self.inner.borrow().has_errors_or_delayed_bugs()
|
||||
}
|
||||
|
||||
pub fn print_error_count(&self, registry: &Registry) {
|
||||
pub fn print_error_count(&self) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
||||
// Any stashed diagnostics should have been handled by
|
||||
|
|
@ -1014,7 +1021,7 @@ impl<'a> DiagCtxtHandle<'a> {
|
|||
.emitted_diagnostic_codes
|
||||
.iter()
|
||||
.filter_map(|&code| {
|
||||
if registry.try_find_description(code).is_ok() {
|
||||
if inner.registry.try_find_description(code).is_ok() {
|
||||
Some(code.to_string())
|
||||
} else {
|
||||
None
|
||||
|
|
@ -1075,10 +1082,10 @@ impl<'a> DiagCtxtHandle<'a> {
|
|||
}
|
||||
|
||||
pub fn emit_future_breakage_report(&self) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let inner = &mut *self.inner.borrow_mut();
|
||||
let diags = std::mem::take(&mut inner.future_breakage_diagnostics);
|
||||
if !diags.is_empty() {
|
||||
inner.emitter.emit_future_breakage_report(diags);
|
||||
inner.emitter.emit_future_breakage_report(diags, &inner.registry);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1409,6 +1416,7 @@ impl DiagCtxtInner {
|
|||
fn new(emitter: Box<DynEmitter>) -> Self {
|
||||
Self {
|
||||
flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() },
|
||||
registry: Registry::new(&[]),
|
||||
err_guars: Vec::new(),
|
||||
lint_err_guars: Vec::new(),
|
||||
delayed_bugs: Vec::new(),
|
||||
|
|
@ -1582,7 +1590,7 @@ impl DiagCtxtInner {
|
|||
}
|
||||
self.has_printed = true;
|
||||
|
||||
self.emitter.emit_diagnostic(diagnostic);
|
||||
self.emitter.emit_diagnostic(diagnostic, &self.registry);
|
||||
}
|
||||
|
||||
if is_error {
|
||||
|
|
@ -1695,7 +1703,13 @@ impl DiagCtxtInner {
|
|||
// eventually happened.
|
||||
assert!(self.stashed_diagnostics.is_empty());
|
||||
|
||||
if !self.err_guars.is_empty() {
|
||||
// If an error happened already. We shouldn't expose delayed bugs.
|
||||
return;
|
||||
}
|
||||
|
||||
if self.delayed_bugs.is_empty() {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -990,7 +990,7 @@ pub fn parse_ast_fragment<'a>(
|
|||
}
|
||||
}
|
||||
AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?),
|
||||
AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_alt(
|
||||
AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::Yes,
|
||||
|
|
|
|||
|
|
@ -505,6 +505,8 @@ declare_features! (
|
|||
(incomplete, generic_const_items, "1.73.0", Some(113521)),
|
||||
/// Allows registering static items globally, possibly across crates, to iterate over at runtime.
|
||||
(unstable, global_registration, "1.80.0", Some(125119)),
|
||||
/// Allows using guards in patterns.
|
||||
(incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)),
|
||||
/// Allows using `..=X` as a patterns in slices.
|
||||
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
|
||||
/// Allows `if let` guard in match arms.
|
||||
|
|
|
|||
|
|
@ -279,7 +279,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
} else {
|
||||
let mut err = self.dcx().create_err(err);
|
||||
if suggest_constraining_type_param(
|
||||
tcx, generics, &mut err, &qself_str, &trait_ref, None, None,
|
||||
tcx,
|
||||
generics,
|
||||
&mut err,
|
||||
&qself_str,
|
||||
&trait_ref,
|
||||
Some(best_trait),
|
||||
None,
|
||||
) && !identically_named
|
||||
{
|
||||
// We suggested constraining a type parameter, but the associated item on it
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
|||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_data_structures::{base_n, flock};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize};
|
||||
use rustc_middle::bug;
|
||||
use rustc_session::config::CrateType;
|
||||
|
|
@ -212,9 +211,9 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu
|
|||
/// The garbage collection will take care of it.
|
||||
///
|
||||
/// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph
|
||||
pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuaranteed> {
|
||||
pub(crate) fn prepare_session_directory(sess: &Session) {
|
||||
if sess.opts.incremental.is_none() {
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
|
||||
let _timer = sess.timer("incr_comp_prepare_session_directory");
|
||||
|
|
@ -224,7 +223,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara
|
|||
// {incr-comp-dir}/{crate-name-and-disambiguator}
|
||||
let crate_dir = crate_path(sess);
|
||||
debug!("crate-dir: {}", crate_dir.display());
|
||||
create_dir(sess, &crate_dir, "crate")?;
|
||||
create_dir(sess, &crate_dir, "crate");
|
||||
|
||||
// Hack: canonicalize the path *after creating the directory*
|
||||
// because, on windows, long paths can cause problems;
|
||||
|
|
@ -233,7 +232,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara
|
|||
let crate_dir = match try_canonicalize(&crate_dir) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
return Err(sess.dcx().emit_err(errors::CanonicalizePath { path: crate_dir, err }));
|
||||
sess.dcx().emit_fatal(errors::CanonicalizePath { path: crate_dir, err });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -248,11 +247,11 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara
|
|||
|
||||
// Lock the new session directory. If this fails, return an
|
||||
// error without retrying
|
||||
let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir)?;
|
||||
let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir);
|
||||
|
||||
// Now that we have the lock, we can actually create the session
|
||||
// directory
|
||||
create_dir(sess, &session_dir, "session")?;
|
||||
create_dir(sess, &session_dir, "session");
|
||||
|
||||
// Find a suitable source directory to copy from. Ignore those that we
|
||||
// have already tried before.
|
||||
|
|
@ -266,7 +265,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara
|
|||
);
|
||||
|
||||
sess.init_incr_comp_session(session_dir, directory_lock);
|
||||
return Ok(());
|
||||
return;
|
||||
};
|
||||
|
||||
debug!("attempting to copy data from source: {}", source_directory.display());
|
||||
|
|
@ -280,7 +279,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara
|
|||
}
|
||||
|
||||
sess.init_incr_comp_session(session_dir, directory_lock);
|
||||
return Ok(());
|
||||
return;
|
||||
} else {
|
||||
debug!("copying failed - trying next directory");
|
||||
|
||||
|
|
@ -459,21 +458,17 @@ fn generate_session_dir_path(crate_dir: &Path) -> PathBuf {
|
|||
directory_path
|
||||
}
|
||||
|
||||
fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ErrorGuaranteed> {
|
||||
fn create_dir(sess: &Session, path: &Path, dir_tag: &str) {
|
||||
match std_fs::create_dir_all(path) {
|
||||
Ok(()) => {
|
||||
debug!("{} directory created successfully", dir_tag);
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => Err(sess.dcx().emit_err(errors::CreateIncrCompDir { tag: dir_tag, path, err })),
|
||||
Err(err) => sess.dcx().emit_fatal(errors::CreateIncrCompDir { tag: dir_tag, path, err }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate the lock-file and lock it.
|
||||
fn lock_directory(
|
||||
sess: &Session,
|
||||
session_dir: &Path,
|
||||
) -> Result<(flock::Lock, PathBuf), ErrorGuaranteed> {
|
||||
fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) {
|
||||
let lock_file_path = lock_file_path(session_dir);
|
||||
debug!("lock_directory() - lock_file: {}", lock_file_path.display());
|
||||
|
||||
|
|
@ -484,15 +479,15 @@ fn lock_directory(
|
|||
true,
|
||||
) {
|
||||
// the lock should be exclusive
|
||||
Ok(lock) => Ok((lock, lock_file_path)),
|
||||
Ok(lock) => (lock, lock_file_path),
|
||||
Err(lock_err) => {
|
||||
let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err);
|
||||
Err(sess.dcx().emit_err(errors::CreateLock {
|
||||
sess.dcx().emit_fatal(errors::CreateLock {
|
||||
lock_err,
|
||||
session_dir,
|
||||
is_unsupported_lock,
|
||||
is_cargo: rustc_session::utils::was_invoked_from_cargo(),
|
||||
}))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use rustc_serialize::Decodable;
|
|||
use rustc_serialize::opaque::MemDecoder;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::IncrementalStateAssertion;
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use super::data::*;
|
||||
|
|
@ -182,7 +181,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkPr
|
|||
/// If we are not in incremental compilation mode, returns `None`.
|
||||
/// Otherwise, tries to load the query result cache from disk,
|
||||
/// creating an empty cache if it could not be loaded.
|
||||
pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> {
|
||||
pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> {
|
||||
if sess.opts.incremental.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -194,19 +193,19 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> {
|
|||
LoadResult::Ok { data: (bytes, start_pos) } => {
|
||||
let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| {
|
||||
sess.dcx().emit_warn(errors::CorruptFile { path: &path });
|
||||
OnDiskCache::new_empty(sess.source_map())
|
||||
OnDiskCache::new_empty()
|
||||
});
|
||||
Some(cache)
|
||||
}
|
||||
_ => Some(OnDiskCache::new_empty(sess.source_map())),
|
||||
_ => Some(OnDiskCache::new_empty()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Setups the dependency graph by loading an existing graph from disk and set up streaming of a
|
||||
/// new graph to an incremental session directory.
|
||||
pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> {
|
||||
pub fn setup_dep_graph(sess: &Session) -> DepGraph {
|
||||
// `load_dep_graph` can only be called after `prepare_session_directory`.
|
||||
prepare_session_directory(sess)?;
|
||||
prepare_session_directory(sess);
|
||||
|
||||
let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess));
|
||||
|
||||
|
|
@ -222,10 +221,9 @@ pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> {
|
|||
});
|
||||
}
|
||||
|
||||
Ok(res
|
||||
.and_then(|result| {
|
||||
let (prev_graph, prev_work_products) = result.open(sess);
|
||||
build_dep_graph(sess, prev_graph, prev_work_products)
|
||||
})
|
||||
.unwrap_or_else(DepGraph::new_disabled))
|
||||
res.and_then(|result| {
|
||||
let (prev_graph, prev_work_products) = result.open(sess);
|
||||
build_dep_graph(sess, prev_graph, prev_work_products)
|
||||
})
|
||||
.unwrap_or_else(DepGraph::new_disabled)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ use std::sync::Arc;
|
|||
use rustc_ast::{LitKind, MetaItemKind, token};
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::jobserver;
|
||||
use rustc_data_structures::stable_hasher::StableHasher;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::{defer, jobserver};
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
|
||||
use rustc_lint::LintStore;
|
||||
|
|
@ -441,7 +441,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
|||
temps_dir,
|
||||
},
|
||||
bundle,
|
||||
config.registry.clone(),
|
||||
config.registry,
|
||||
locale_resources,
|
||||
config.lint_caps,
|
||||
target,
|
||||
|
|
@ -492,32 +492,34 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
|||
|
||||
// There are two paths out of `f`.
|
||||
// - Normal exit.
|
||||
// - Panic, e.g. triggered by `abort_if_errors`.
|
||||
// - Panic, e.g. triggered by `abort_if_errors` or a fatal error.
|
||||
//
|
||||
// We must run `finish_diagnostics` in both cases.
|
||||
let res = {
|
||||
// If `f` panics, `finish_diagnostics` will run during
|
||||
// unwinding because of the `defer`.
|
||||
let sess_abort_guard = defer(|| {
|
||||
compiler.sess.finish_diagnostics(&config.registry);
|
||||
});
|
||||
let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&compiler)));
|
||||
|
||||
let res = f(&compiler);
|
||||
compiler.sess.finish_diagnostics();
|
||||
|
||||
// If `f` doesn't panic, `finish_diagnostics` will run
|
||||
// normally when `sess_abort_guard` is dropped.
|
||||
drop(sess_abort_guard);
|
||||
|
||||
// If error diagnostics have been emitted, we can't return an
|
||||
// error directly, because the return type of this function
|
||||
// is `R`, not `Result<R, E>`. But we need to communicate the
|
||||
// errors' existence to the caller, otherwise the caller might
|
||||
// mistakenly think that no errors occurred and return a zero
|
||||
// exit code. So we abort (panic) instead, similar to if `f`
|
||||
// had panicked.
|
||||
// If error diagnostics have been emitted, we can't return an
|
||||
// error directly, because the return type of this function
|
||||
// is `R`, not `Result<R, E>`. But we need to communicate the
|
||||
// errors' existence to the caller, otherwise the caller might
|
||||
// mistakenly think that no errors occurred and return a zero
|
||||
// exit code. So we abort (panic) instead, similar to if `f`
|
||||
// had panicked.
|
||||
if res.is_ok() {
|
||||
compiler.sess.dcx().abort_if_errors();
|
||||
}
|
||||
|
||||
res
|
||||
// Also make sure to flush delayed bugs as if we panicked, the
|
||||
// bugs would be flushed by the Drop impl of DiagCtxt while
|
||||
// unwinding, which would result in an abort with
|
||||
// "panic in a destructor during cleanup".
|
||||
compiler.sess.dcx().flush_delayed();
|
||||
|
||||
let res = match res {
|
||||
Ok(res) => res,
|
||||
// Resume unwinding if a panic happened.
|
||||
Err(err) => std::panic::resume_unwind(err),
|
||||
};
|
||||
|
||||
let prof = compiler.sess.prof.clone();
|
||||
|
|
|
|||
|
|
@ -33,15 +33,15 @@ use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_
|
|||
use rustc_session::search_paths::PathKind;
|
||||
use rustc_session::{Limit, Session};
|
||||
use rustc_span::symbol::{Symbol, sym};
|
||||
use rustc_span::{FileName, SourceFileHash, SourceFileHashAlgorithm};
|
||||
use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm};
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
use rustc_trait_selection::traits;
|
||||
use tracing::{info, instrument};
|
||||
|
||||
use crate::interface::{Compiler, Result};
|
||||
use crate::interface::Compiler;
|
||||
use crate::{errors, proc_macro_decls, util};
|
||||
|
||||
pub(crate) fn parse<'a>(sess: &'a Session) -> Result<ast::Crate> {
|
||||
pub(crate) fn parse<'a>(sess: &'a Session) -> ast::Crate {
|
||||
let krate = sess
|
||||
.time("parse_crate", || {
|
||||
let mut parser = unwrap_or_emit_fatal(match &sess.io.input {
|
||||
|
|
@ -52,13 +52,16 @@ pub(crate) fn parse<'a>(sess: &'a Session) -> Result<ast::Crate> {
|
|||
});
|
||||
parser.parse_crate_mod()
|
||||
})
|
||||
.map_err(|parse_error| parse_error.emit())?;
|
||||
.unwrap_or_else(|parse_error| {
|
||||
let guar: ErrorGuaranteed = parse_error.emit();
|
||||
guar.raise_fatal();
|
||||
});
|
||||
|
||||
if sess.opts.unstable_opts.input_stats {
|
||||
input_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS", "ast-stats-1");
|
||||
}
|
||||
|
||||
Ok(krate)
|
||||
krate
|
||||
}
|
||||
|
||||
fn pre_expansion_lint<'a>(
|
||||
|
|
@ -712,7 +715,7 @@ pub(crate) fn create_global_ctxt<'tcx>(
|
|||
gcx_cell: &'tcx OnceLock<GlobalCtxt<'tcx>>,
|
||||
arena: &'tcx WorkerLocal<Arena<'tcx>>,
|
||||
hir_arena: &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>,
|
||||
) -> Result<&'tcx GlobalCtxt<'tcx>> {
|
||||
) -> &'tcx GlobalCtxt<'tcx> {
|
||||
let sess = &compiler.sess;
|
||||
|
||||
rustc_builtin_macros::cmdline_attrs::inject(
|
||||
|
|
@ -733,7 +736,7 @@ pub(crate) fn create_global_ctxt<'tcx>(
|
|||
sess.cfg_version,
|
||||
);
|
||||
let outputs = util::build_output_filenames(&pre_configured_attrs, sess);
|
||||
let dep_graph = setup_dep_graph(sess)?;
|
||||
let dep_graph = setup_dep_graph(sess);
|
||||
|
||||
let cstore =
|
||||
FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _);
|
||||
|
|
@ -796,7 +799,7 @@ pub(crate) fn create_global_ctxt<'tcx>(
|
|||
feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs))));
|
||||
feed.output_filenames(Arc::new(outputs));
|
||||
});
|
||||
Ok(qcx)
|
||||
qcx
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -906,7 +909,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
|
|||
|
||||
/// Runs the type-checking, region checking and other miscellaneous analysis
|
||||
/// passes on the crate.
|
||||
fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
|
||||
fn analysis(tcx: TyCtxt<'_>, (): ()) {
|
||||
run_required_analyses(tcx);
|
||||
|
||||
let sess = tcx.sess;
|
||||
|
|
@ -920,7 +923,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
|
|||
// But we exclude lint errors from this, because lint errors are typically
|
||||
// less serious and we're more likely to want to continue (#87337).
|
||||
if let Some(guar) = sess.dcx().has_errors_excluding_lint_errors() {
|
||||
return Err(guar);
|
||||
guar.raise_fatal();
|
||||
}
|
||||
|
||||
sess.time("misc_checking_3", || {
|
||||
|
|
@ -1048,8 +1051,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used
|
||||
|
|
@ -1091,12 +1092,12 @@ fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) {
|
|||
pub(crate) fn start_codegen<'tcx>(
|
||||
codegen_backend: &dyn CodegenBackend,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Result<Box<dyn Any>> {
|
||||
) -> Box<dyn Any> {
|
||||
// Don't do code generation if there were any errors. Likewise if
|
||||
// there were any delayed bugs, because codegen will likely cause
|
||||
// more ICEs, obscuring the original problem.
|
||||
if let Some(guar) = tcx.sess.dcx().has_errors_or_delayed_bugs() {
|
||||
return Err(guar);
|
||||
guar.raise_fatal();
|
||||
}
|
||||
|
||||
// Hook for UI tests.
|
||||
|
|
@ -1124,7 +1125,7 @@ pub(crate) fn start_codegen<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
Ok(codegen)
|
||||
codegen
|
||||
}
|
||||
|
||||
fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use rustc_session::Session;
|
|||
use rustc_session::config::{self, OutputFilenames, OutputType};
|
||||
|
||||
use crate::errors::FailedWritingFile;
|
||||
use crate::interface::{Compiler, Result};
|
||||
use crate::interface::Compiler;
|
||||
use crate::passes;
|
||||
|
||||
/// Represent the result of a query.
|
||||
|
|
@ -27,19 +27,17 @@ use crate::passes;
|
|||
/// [`compute`]: Self::compute
|
||||
pub struct Query<T> {
|
||||
/// `None` means no value has been computed yet.
|
||||
result: RefCell<Option<Result<Steal<T>>>>,
|
||||
result: RefCell<Option<Steal<T>>>,
|
||||
}
|
||||
|
||||
impl<T> Query<T> {
|
||||
fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<QueryResult<'_, T>> {
|
||||
RefMut::filter_map(
|
||||
fn compute<F: FnOnce() -> T>(&self, f: F) -> QueryResult<'_, T> {
|
||||
QueryResult(RefMut::map(
|
||||
self.result.borrow_mut(),
|
||||
|r: &mut Option<Result<Steal<T>>>| -> Option<&mut Steal<T>> {
|
||||
r.get_or_insert_with(|| f().map(Steal::new)).as_mut().ok()
|
||||
|r: &mut Option<Steal<T>>| -> &mut Steal<T> {
|
||||
r.get_or_insert_with(|| Steal::new(f()))
|
||||
},
|
||||
)
|
||||
.map_err(|r| *r.as_ref().unwrap().as_ref().map(|_| ()).unwrap_err())
|
||||
.map(QueryResult)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,13 +93,13 @@ impl<'tcx> Queries<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse(&self) -> Result<QueryResult<'_, ast::Crate>> {
|
||||
pub fn parse(&self) -> QueryResult<'_, ast::Crate> {
|
||||
self.parse.compute(|| passes::parse(&self.compiler.sess))
|
||||
}
|
||||
|
||||
pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'tcx, &'tcx GlobalCtxt<'tcx>>> {
|
||||
pub fn global_ctxt(&'tcx self) -> QueryResult<'tcx, &'tcx GlobalCtxt<'tcx>> {
|
||||
self.gcx.compute(|| {
|
||||
let krate = self.parse()?.steal();
|
||||
let krate = self.parse().steal();
|
||||
|
||||
passes::create_global_ctxt(
|
||||
self.compiler,
|
||||
|
|
@ -126,8 +124,8 @@ impl Linker {
|
|||
pub fn codegen_and_build_linker(
|
||||
tcx: TyCtxt<'_>,
|
||||
codegen_backend: &dyn CodegenBackend,
|
||||
) -> Result<Linker> {
|
||||
let ongoing_codegen = passes::start_codegen(codegen_backend, tcx)?;
|
||||
) -> Linker {
|
||||
let ongoing_codegen = passes::start_codegen(codegen_backend, tcx);
|
||||
|
||||
// This must run after monomorphization so that all generic types
|
||||
// have been instantiated.
|
||||
|
|
@ -141,7 +139,7 @@ impl Linker {
|
|||
tcx.sess.code_stats.print_vtable_sizes(crate_name);
|
||||
}
|
||||
|
||||
Ok(Linker {
|
||||
Linker {
|
||||
dep_graph: tcx.dep_graph.clone(),
|
||||
output_filenames: Arc::clone(tcx.output_filenames(())),
|
||||
crate_hash: if tcx.needs_crate_hash() {
|
||||
|
|
@ -150,16 +148,17 @@ impl Linker {
|
|||
None
|
||||
},
|
||||
ongoing_codegen,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) -> Result<()> {
|
||||
let (codegen_results, work_products) =
|
||||
codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames);
|
||||
pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) {
|
||||
let (codegen_results, work_products) = sess.time("finish_ongoing_codegen", || {
|
||||
codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames)
|
||||
});
|
||||
|
||||
if let Some(guar) = sess.dcx().has_errors() {
|
||||
return Err(guar);
|
||||
}
|
||||
sess.dcx().abort_if_errors();
|
||||
|
||||
let _timer = sess.timer("link");
|
||||
|
||||
sess.time("serialize_work_products", || {
|
||||
rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products)
|
||||
|
|
@ -178,7 +177,7 @@ impl Linker {
|
|||
.keys()
|
||||
.any(|&i| i == OutputType::Exe || i == OutputType::Metadata)
|
||||
{
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
|
||||
if sess.opts.unstable_opts.no_link {
|
||||
|
|
@ -189,10 +188,10 @@ impl Linker {
|
|||
&codegen_results,
|
||||
&*self.output_filenames,
|
||||
)
|
||||
.map_err(|error| {
|
||||
.unwrap_or_else(|error| {
|
||||
sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error })
|
||||
})?;
|
||||
return Ok(());
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let _timer = sess.prof.verbose_generic_activity("link_crate");
|
||||
|
|
|
|||
|
|
@ -359,7 +359,6 @@ lint_improper_ctypes_128bit = 128-bit integers don't currently have a known stab
|
|||
lint_improper_ctypes_array_help = consider passing a pointer to the array
|
||||
|
||||
lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe
|
||||
lint_improper_ctypes_box = box cannot be represented as a single pointer
|
||||
|
||||
lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead
|
||||
|
||||
|
|
@ -377,7 +376,9 @@ lint_improper_ctypes_enum_repr_help =
|
|||
lint_improper_ctypes_enum_repr_reason = enum has no representation hint
|
||||
lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead
|
||||
|
||||
lint_improper_ctypes_fnptr_indirect_reason = the function pointer to `{$ty}` is FFI-unsafe due to `{$inner_ty}`
|
||||
lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention
|
||||
|
||||
lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive
|
||||
lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants
|
||||
|
||||
|
|
@ -388,7 +389,11 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent
|
|||
lint_improper_ctypes_pat_help = consider using the base type instead
|
||||
|
||||
lint_improper_ctypes_pat_reason = pattern types have no C equivalent
|
||||
lint_improper_ctypes_slice_help = consider using a raw pointer instead
|
||||
|
||||
lint_improper_ctypes_sized_ptr_to_unsafe_type =
|
||||
this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout
|
||||
|
||||
lint_improper_ctypes_slice_help = consider using a raw pointer to the slice's first element (and a length) instead
|
||||
|
||||
lint_improper_ctypes_slice_reason = slices have no C equivalent
|
||||
lint_improper_ctypes_str_help = consider using `*const u8` and a length instead
|
||||
|
|
@ -414,6 +419,10 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re
|
|||
lint_improper_ctypes_union_layout_reason = this union has unspecified layout
|
||||
lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive
|
||||
|
||||
lint_improper_ctypes_unsized_box = this box for an unsized type contains metadata, which makes it incompatible with a C pointer
|
||||
lint_improper_ctypes_unsized_ptr = this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer
|
||||
lint_improper_ctypes_unsized_ref = this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
|
||||
|
||||
lint_incomplete_include =
|
||||
include macro expected single expression in source
|
||||
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
|
|||
|
||||
fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) {
|
||||
self.check_id(lt.id);
|
||||
ast_visit::walk_lifetime(self, lt);
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) {
|
||||
|
|
@ -259,6 +260,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
|
|||
|
||||
fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
|
||||
lint_callback!(self, check_attribute, attr);
|
||||
ast_visit::walk_attribute(self, attr);
|
||||
}
|
||||
|
||||
fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use crate::lints::{
|
|||
use crate::{EarlyContext, EarlyLintPass, LintContext};
|
||||
|
||||
declare_lint! {
|
||||
#[allow(text_direction_codepoint_in_literal)]
|
||||
/// The `text_direction_codepoint_in_literal` lint detects Unicode codepoints that change the
|
||||
/// visual representation of text on screen in a way that does not correspond to their on
|
||||
/// memory representation.
|
||||
|
|
|
|||
|
|
@ -1851,13 +1851,44 @@ pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> {
|
|||
pub right: Span,
|
||||
}
|
||||
|
||||
pub(crate) struct ImproperCTypesLayer<'a> {
|
||||
pub ty: Ty<'a>,
|
||||
pub inner_ty: Option<Ty<'a>>,
|
||||
pub note: DiagMessage,
|
||||
pub span_note: Option<Span>,
|
||||
pub help: Option<DiagMessage>,
|
||||
}
|
||||
|
||||
impl<'a> Subdiagnostic for ImproperCTypesLayer<'a> {
|
||||
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
self,
|
||||
diag: &mut Diag<'_, G>,
|
||||
f: &F,
|
||||
) {
|
||||
diag.arg("ty", self.ty);
|
||||
if let Some(ty) = self.inner_ty {
|
||||
diag.arg("inner_ty", ty);
|
||||
}
|
||||
|
||||
if let Some(help) = self.help {
|
||||
let msg = f(diag, help.into());
|
||||
diag.help(msg);
|
||||
}
|
||||
|
||||
let msg = f(diag, self.note.into());
|
||||
diag.note(msg);
|
||||
if let Some(note) = self.span_note {
|
||||
let msg = f(diag, fluent::lint_note.into());
|
||||
diag.span_note(note, msg);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ImproperCTypes<'a> {
|
||||
pub ty: Ty<'a>,
|
||||
pub desc: &'a str,
|
||||
pub label: Span,
|
||||
pub help: Option<DiagMessage>,
|
||||
pub note: DiagMessage,
|
||||
pub span_note: Option<Span>,
|
||||
pub reasons: Vec<ImproperCTypesLayer<'a>>,
|
||||
}
|
||||
|
||||
// Used because of the complexity of Option<DiagMessage>, DiagMessage, and Option<Span>
|
||||
|
|
@ -1867,12 +1898,8 @@ impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> {
|
|||
diag.arg("ty", self.ty);
|
||||
diag.arg("desc", self.desc);
|
||||
diag.span_label(self.label, fluent::lint_label);
|
||||
if let Some(help) = self.help {
|
||||
diag.help(help);
|
||||
}
|
||||
diag.note(self.note);
|
||||
if let Some(note) = self.span_note {
|
||||
diag.span_note(note, fluent::lint_note);
|
||||
for reason in self.reasons.into_iter() {
|
||||
diag.subdiagnostic(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ mod improper_ctypes;
|
|||
use crate::lints::{
|
||||
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
|
||||
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
|
||||
AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
|
||||
InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
|
||||
UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons,
|
||||
VariantSizeDifferencesDiag,
|
||||
AtomicOrderingStore, ImproperCTypes, ImproperCTypesLayer, InvalidAtomicOrderingDiag,
|
||||
InvalidNanComparisons, InvalidNanComparisonsSuggestion,
|
||||
UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion,
|
||||
UnusedComparisons, VariantSizeDifferencesDiag,
|
||||
};
|
||||
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
|
||||
|
||||
|
|
@ -727,7 +727,109 @@ struct CTypesVisitorState<'tcx> {
|
|||
enum FfiResult<'tcx> {
|
||||
FfiSafe,
|
||||
FfiPhantom(Ty<'tcx>),
|
||||
FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
|
||||
FfiUnsafe {
|
||||
ty: Ty<'tcx>,
|
||||
reason: DiagMessage,
|
||||
help: Option<DiagMessage>,
|
||||
},
|
||||
FfiUnsafeWrapper {
|
||||
ty: Ty<'tcx>,
|
||||
reason: DiagMessage,
|
||||
help: Option<DiagMessage>,
|
||||
wrapped: Box<FfiResult<'tcx>>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Determine if a type is sized or not, and wether it affects references/pointers/boxes to it
|
||||
#[derive(Clone, Copy)]
|
||||
enum TypeSizedness {
|
||||
/// type of definite size (pointers are C-compatible)
|
||||
Definite,
|
||||
/// unsized type because it includes an opaque/foreign type (pointers are C-compatible)
|
||||
UnsizedWithExternType,
|
||||
/// unsized type for other reasons (slice, string, dyn Trait, closure, ...) (pointers are not C-compatible)
|
||||
UnsizedWithMetadata,
|
||||
}
|
||||
|
||||
/// Is this type unsized because it contains (or is) a foreign type?
|
||||
/// (Returns Err if the type happens to be sized after all)
|
||||
fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> TypeSizedness {
|
||||
let tcx = cx.tcx;
|
||||
|
||||
if ty.is_sized(tcx, cx.typing_env()) {
|
||||
TypeSizedness::Definite
|
||||
} else {
|
||||
match ty.kind() {
|
||||
ty::Slice(_) => TypeSizedness::UnsizedWithMetadata,
|
||||
ty::Str => TypeSizedness::UnsizedWithMetadata,
|
||||
ty::Dynamic(..) => TypeSizedness::UnsizedWithMetadata,
|
||||
ty::Foreign(..) => TypeSizedness::UnsizedWithExternType,
|
||||
// While opaque types are checked for earlier, if a projection in a struct field
|
||||
// normalizes to an opaque type, then it will reach this branch.
|
||||
ty::Alias(ty::Opaque, ..) => todo!("We... don't know enough about this type yet?"),
|
||||
ty::Adt(def, args) => {
|
||||
// for now assume: boxes and phantoms don't mess with this
|
||||
match def.adt_kind() {
|
||||
AdtKind::Union | AdtKind::Enum => {
|
||||
bug!("unions and enums are necessarily sized")
|
||||
}
|
||||
AdtKind::Struct => {
|
||||
if let Some(sym::cstring_type | sym::cstr_type) =
|
||||
tcx.get_diagnostic_name(def.did())
|
||||
{
|
||||
return TypeSizedness::UnsizedWithMetadata;
|
||||
}
|
||||
// FIXME: how do we deal with non-exhaustive unsized structs/unions?
|
||||
|
||||
if def.non_enum_variant().fields.is_empty() {
|
||||
bug!("an empty struct is necessarily sized");
|
||||
}
|
||||
|
||||
let variant = def.non_enum_variant();
|
||||
|
||||
// only the last field may be unsized
|
||||
let n_fields = variant.fields.len();
|
||||
let last_field = &variant.fields[(n_fields - 1).into()];
|
||||
let field_ty = last_field.ty(cx.tcx, args);
|
||||
let field_ty = cx
|
||||
.tcx
|
||||
.try_normalize_erasing_regions(cx.typing_env(), field_ty)
|
||||
.unwrap_or(field_ty);
|
||||
match get_type_sizedness(cx, field_ty) {
|
||||
s @ (TypeSizedness::UnsizedWithMetadata
|
||||
| TypeSizedness::UnsizedWithExternType) => s,
|
||||
TypeSizedness::Definite => {
|
||||
bug!("failed to find the reason why struct `{:?}` is unsized", ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::Tuple(tuple) => {
|
||||
// only the last field may be unsized
|
||||
let n_fields = tuple.len();
|
||||
let field_ty: Ty<'tcx> = tuple[n_fields - 1];
|
||||
//let field_ty = last_field.ty(cx.tcx, args);
|
||||
let field_ty = cx
|
||||
.tcx
|
||||
.try_normalize_erasing_regions(cx.typing_env(), field_ty)
|
||||
.unwrap_or(field_ty);
|
||||
match get_type_sizedness(cx, field_ty) {
|
||||
s @ (TypeSizedness::UnsizedWithMetadata
|
||||
| TypeSizedness::UnsizedWithExternType) => s,
|
||||
TypeSizedness::Definite => {
|
||||
bug!("failed to find the reason why tuple `{:?}` is unsized", ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
ty => {
|
||||
bug!(
|
||||
"we shouldn't be trying to determine if this is unsized for a reason or another: `{:?}`",
|
||||
ty
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
|
||||
|
|
@ -764,7 +866,7 @@ fn ty_is_known_nonnull<'tcx>(
|
|||
match ty.kind() {
|
||||
ty::FnPtr(..) => true,
|
||||
ty::Ref(..) => true,
|
||||
ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
|
||||
ty::Adt(def, _) if def.is_box() => true,
|
||||
ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => {
|
||||
let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
|
||||
|
||||
|
|
@ -933,12 +1035,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
/// Check if the type is array and emit an unsafe type lint.
|
||||
fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
|
||||
if let ty::Array(..) = ty.kind() {
|
||||
self.emit_ffi_unsafe_type_lint(
|
||||
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
|
||||
ty,
|
||||
sp,
|
||||
fluent::lint_improper_ctypes_array_reason,
|
||||
Some(fluent::lint_improper_ctypes_array_help),
|
||||
);
|
||||
note: fluent::lint_improper_ctypes_array_reason,
|
||||
help: Some(fluent::lint_improper_ctypes_array_help),
|
||||
inner_ty: None,
|
||||
span_note: None,
|
||||
}]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
@ -995,9 +1098,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
all_phantom &= match self.check_field_type_for_ffi(acc, field, args) {
|
||||
FfiSafe => false,
|
||||
// `()` fields are FFI-safe!
|
||||
FfiUnsafe { ty, .. } if ty.is_unit() => false,
|
||||
FfiUnsafe { ty, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false,
|
||||
FfiPhantom(..) => true,
|
||||
r @ FfiUnsafe { .. } => return r,
|
||||
r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1031,16 +1134,47 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
|
||||
match *ty.kind() {
|
||||
ty::Adt(def, args) => {
|
||||
if let Some(boxed) = ty.boxed_ty()
|
||||
&& matches!(self.mode, CItemKind::Definition)
|
||||
{
|
||||
if boxed.is_sized(tcx, self.cx.typing_env()) {
|
||||
return FfiSafe;
|
||||
if let Some(inner_ty) = ty.boxed_ty() {
|
||||
if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite =
|
||||
get_type_sizedness(self.cx, inner_ty)
|
||||
{
|
||||
// discussion on declaration vs definition:
|
||||
// see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
|
||||
// of this `match *ty.kind()` block
|
||||
if matches!(self.mode, CItemKind::Definition) {
|
||||
return FfiSafe;
|
||||
} else {
|
||||
let inner_res = self.check_type_for_ffi(acc, inner_ty);
|
||||
return match inner_res {
|
||||
FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
|
||||
wrapped: Box::new(inner_res),
|
||||
help: None,
|
||||
},
|
||||
_ => inner_res,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
let help = match inner_ty.kind() {
|
||||
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
|
||||
ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
|
||||
ty::Adt(def, _)
|
||||
if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
|
||||
&& matches!(
|
||||
tcx.get_diagnostic_name(def.did()),
|
||||
Some(sym::cstring_type | sym::cstr_type)
|
||||
)
|
||||
&& !acc.base_ty.is_mutable_ptr() =>
|
||||
{
|
||||
Some(fluent::lint_improper_ctypes_cstr_help)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
return FfiUnsafe {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_box,
|
||||
help: None,
|
||||
reason: fluent::lint_improper_ctypes_unsized_box,
|
||||
help,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1196,15 +1330,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
help: Some(fluent::lint_improper_ctypes_tuple_help),
|
||||
},
|
||||
|
||||
ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
|
||||
if {
|
||||
matches!(self.mode, CItemKind::Definition)
|
||||
&& ty.is_sized(self.cx.tcx, self.cx.typing_env())
|
||||
} =>
|
||||
{
|
||||
FfiSafe
|
||||
}
|
||||
|
||||
ty::RawPtr(ty, _)
|
||||
if match ty.kind() {
|
||||
ty::Tuple(tuple) => tuple.is_empty(),
|
||||
|
|
@ -1214,7 +1339,70 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
FfiSafe
|
||||
}
|
||||
|
||||
ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty),
|
||||
ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => {
|
||||
if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite =
|
||||
get_type_sizedness(self.cx, inner_ty)
|
||||
{
|
||||
// there's a nuance on what this lint should do for
|
||||
// function definitions (`extern "C" fn fn_name(...) {...}`)
|
||||
// versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
|
||||
// This is touched upon in https://github.com/rust-lang/rust/issues/66220
|
||||
// and https://github.com/rust-lang/rust/pull/72700
|
||||
//
|
||||
// The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
|
||||
// (which has a stable layout) but points to FFI-unsafe type, is it safe?
|
||||
// On one hand, the function's ABI will match that of a similar C-declared function API,
|
||||
// on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
|
||||
// In this code, the opinion on is split between function declarations and function definitions,
|
||||
// with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
|
||||
// For declarations, we see this as unsafe, but for definitions, we see this as safe.
|
||||
//
|
||||
// For extern function declarations, the actual definition of the function is written somewhere else,
|
||||
// meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side)
|
||||
// For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
|
||||
// and having the full type information is necessary to compile the function.
|
||||
if matches!(self.mode, CItemKind::Definition) {
|
||||
return FfiSafe;
|
||||
} else if matches!(ty.kind(), ty::RawPtr(..))
|
||||
&& matches!(inner_ty.kind(), ty::Tuple(tuple) if tuple.is_empty())
|
||||
{
|
||||
FfiSafe
|
||||
} else {
|
||||
let inner_res = self.check_type_for_ffi(acc, inner_ty);
|
||||
return match inner_res {
|
||||
FfiSafe => inner_res,
|
||||
_ => FfiUnsafeWrapper {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
|
||||
wrapped: Box::new(inner_res),
|
||||
help: None,
|
||||
},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
let help = match inner_ty.kind() {
|
||||
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
|
||||
ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
|
||||
ty::Adt(def, _)
|
||||
if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
|
||||
&& matches!(
|
||||
tcx.get_diagnostic_name(def.did()),
|
||||
Some(sym::cstring_type | sym::cstr_type)
|
||||
)
|
||||
&& !acc.base_ty.is_mutable_ptr() =>
|
||||
{
|
||||
Some(fluent::lint_improper_ctypes_cstr_help)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let reason = match ty.kind() {
|
||||
ty::RawPtr(..) => fluent::lint_improper_ctypes_unsized_ptr,
|
||||
ty::Ref(..) => fluent::lint_improper_ctypes_unsized_ref,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
FfiUnsafe { ty, reason, help }
|
||||
}
|
||||
}
|
||||
|
||||
ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty),
|
||||
|
||||
|
|
@ -1232,7 +1420,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
for arg in sig.inputs() {
|
||||
match self.check_type_for_ffi(acc, *arg) {
|
||||
FfiSafe => {}
|
||||
r => return r,
|
||||
r => {
|
||||
return FfiUnsafeWrapper {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_fnptr_indirect_reason,
|
||||
help: None,
|
||||
wrapped: Box::new(r),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1241,7 +1436,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
return FfiSafe;
|
||||
}
|
||||
|
||||
self.check_type_for_ffi(acc, ret_ty)
|
||||
match self.check_type_for_ffi(acc, ret_ty) {
|
||||
r @ (FfiSafe | FfiPhantom(_)) => r,
|
||||
r => FfiUnsafeWrapper {
|
||||
ty: ty.clone(),
|
||||
reason: fluent::lint_improper_ctypes_fnptr_indirect_reason,
|
||||
help: None,
|
||||
wrapped: Box::new(r),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
ty::Foreign(..) => FfiSafe,
|
||||
|
|
@ -1278,8 +1481,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
&mut self,
|
||||
ty: Ty<'tcx>,
|
||||
sp: Span,
|
||||
note: DiagMessage,
|
||||
help: Option<DiagMessage>,
|
||||
mut reasons: Vec<ImproperCTypesLayer<'tcx>>,
|
||||
) {
|
||||
let lint = match self.mode {
|
||||
CItemKind::Declaration => IMPROPER_CTYPES,
|
||||
|
|
@ -1289,21 +1491,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
CItemKind::Declaration => "block",
|
||||
CItemKind::Definition => "fn",
|
||||
};
|
||||
let span_note = if let ty::Adt(def, _) = ty.kind()
|
||||
&& let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
|
||||
{
|
||||
Some(sp)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.cx.emit_span_lint(lint, sp, ImproperCTypes {
|
||||
ty,
|
||||
desc,
|
||||
label: sp,
|
||||
help,
|
||||
note,
|
||||
span_note,
|
||||
});
|
||||
for reason in reasons.iter_mut() {
|
||||
reason.span_note = if let ty::Adt(def, _) = reason.ty.kind()
|
||||
&& let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
|
||||
{
|
||||
Some(sp)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
self.cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons });
|
||||
}
|
||||
|
||||
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
|
||||
|
|
@ -1332,7 +1530,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
.visit_with(&mut ProhibitOpaqueTypes)
|
||||
.break_value()
|
||||
{
|
||||
self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
|
||||
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
|
||||
ty,
|
||||
note: fluent::lint_improper_ctypes_opaque,
|
||||
span_note: Some(sp),
|
||||
help: None,
|
||||
inner_ty: None,
|
||||
}]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
@ -1371,15 +1575,71 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
match self.check_type_for_ffi(&mut acc, ty) {
|
||||
FfiResult::FfiSafe => {}
|
||||
FfiResult::FfiPhantom(ty) => {
|
||||
self.emit_ffi_unsafe_type_lint(
|
||||
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
|
||||
ty,
|
||||
sp,
|
||||
fluent::lint_improper_ctypes_only_phantomdata,
|
||||
None,
|
||||
);
|
||||
note: fluent::lint_improper_ctypes_only_phantomdata,
|
||||
span_note: None, // filled later
|
||||
help: None,
|
||||
inner_ty: None,
|
||||
}]);
|
||||
}
|
||||
FfiResult::FfiUnsafe { ty, reason, help } => {
|
||||
self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
|
||||
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
|
||||
ty,
|
||||
help,
|
||||
note: reason,
|
||||
span_note: None, // filled later
|
||||
inner_ty: None,
|
||||
}]);
|
||||
}
|
||||
ffir @ FfiResult::FfiUnsafeWrapper { .. } => {
|
||||
let mut ffiresult_recursor = ControlFlow::Continue(&ffir);
|
||||
let mut cimproper_layers: Vec<ImproperCTypesLayer<'tcx>> = vec![];
|
||||
|
||||
// this whole while block converts the arbitrarily-deep
|
||||
// FfiResult stack to an ImproperCTypesLayer Vec
|
||||
while let ControlFlow::Continue(ref ffir_rec) = ffiresult_recursor {
|
||||
match ffir_rec {
|
||||
FfiResult::FfiPhantom(ty) => {
|
||||
if let Some(layer) = cimproper_layers.last_mut() {
|
||||
layer.inner_ty = Some(ty.clone());
|
||||
}
|
||||
cimproper_layers.push(ImproperCTypesLayer {
|
||||
ty: ty.clone(),
|
||||
inner_ty: None,
|
||||
help: None,
|
||||
note: fluent::lint_improper_ctypes_only_phantomdata,
|
||||
span_note: None, // filled later
|
||||
});
|
||||
ffiresult_recursor = ControlFlow::Break(());
|
||||
}
|
||||
FfiResult::FfiUnsafe { ty, reason, help }
|
||||
| FfiResult::FfiUnsafeWrapper { ty, reason, help, .. } => {
|
||||
if let Some(layer) = cimproper_layers.last_mut() {
|
||||
layer.inner_ty = Some(ty.clone());
|
||||
}
|
||||
cimproper_layers.push(ImproperCTypesLayer {
|
||||
ty: ty.clone(),
|
||||
inner_ty: None,
|
||||
help: help.clone(),
|
||||
note: reason.clone(),
|
||||
span_note: None, // filled later
|
||||
});
|
||||
|
||||
if let FfiResult::FfiUnsafeWrapper { wrapped, .. } = ffir_rec {
|
||||
ffiresult_recursor = ControlFlow::Continue(wrapped.as_ref());
|
||||
} else {
|
||||
ffiresult_recursor = ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
FfiResult::FfiSafe => {
|
||||
bug!("malformed FfiResult stack: it should be unsafe all the way down")
|
||||
}
|
||||
};
|
||||
}
|
||||
// should always have at least one type
|
||||
let last_ty = cimproper_layers.last().unwrap().ty.clone();
|
||||
self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1235,7 +1235,7 @@ impl EarlyLintPass for UnusedParens {
|
|||
self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
|
||||
},
|
||||
// Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
|
||||
Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
|
||||
Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
|
||||
// Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
|
||||
// Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
|
||||
Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
|
||||
|
|
|
|||
|
|
@ -3929,6 +3929,7 @@ declare_lint! {
|
|||
}
|
||||
|
||||
declare_lint! {
|
||||
#[allow(text_direction_codepoint_in_literal)]
|
||||
/// The `text_direction_codepoint_in_comment` lint detects Unicode codepoints in comments that
|
||||
/// change the visual representation of text on screen in a way that does not correspond to
|
||||
/// their on memory representation.
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ rustc_queries! {
|
|||
}
|
||||
|
||||
/// The root query triggering all analysis passes like typeck or borrowck.
|
||||
query analysis(key: ()) -> Result<(), ErrorGuaranteed> {
|
||||
query analysis(key: ()) {
|
||||
eval_always
|
||||
desc { "running analysis passes on this crate" }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use rustc_session::Session;
|
|||
use rustc_span::hygiene::{
|
||||
ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext, SyntaxContextData,
|
||||
};
|
||||
use rustc_span::source_map::{SourceMap, Spanned};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{
|
||||
BytePos, CachingSourceMapView, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span,
|
||||
SpanDecoder, SpanEncoder, StableSourceFileId, Symbol,
|
||||
|
|
@ -49,7 +49,7 @@ const SYMBOL_PREINTERNED: u8 = 2;
|
|||
/// previous compilation session. This data will eventually include the results
|
||||
/// of a few selected queries (like `typeck` and `mir_optimized`) and
|
||||
/// any side effects that have been emitted during a query.
|
||||
pub struct OnDiskCache<'sess> {
|
||||
pub struct OnDiskCache {
|
||||
// The complete cache data in serialized form.
|
||||
serialized_data: RwLock<Option<Mmap>>,
|
||||
|
||||
|
|
@ -57,7 +57,6 @@ pub struct OnDiskCache<'sess> {
|
|||
// session.
|
||||
current_side_effects: Lock<FxHashMap<DepNodeIndex, QuerySideEffects>>,
|
||||
|
||||
source_map: &'sess SourceMap,
|
||||
file_index_to_stable_id: FxHashMap<SourceFileIndex, EncodedSourceFileId>,
|
||||
|
||||
// Caches that are populated lazily during decoding.
|
||||
|
|
@ -151,12 +150,12 @@ impl EncodedSourceFileId {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'sess> OnDiskCache<'sess> {
|
||||
impl OnDiskCache {
|
||||
/// Creates a new `OnDiskCache` instance from the serialized data in `data`.
|
||||
///
|
||||
/// The serialized cache has some basic integrity checks, if those checks indicate that the
|
||||
/// on-disk data is corrupt, an error is returned.
|
||||
pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Result<Self, ()> {
|
||||
pub fn new(sess: &Session, data: Mmap, start_pos: usize) -> Result<Self, ()> {
|
||||
assert!(sess.opts.incremental.is_some());
|
||||
|
||||
let mut decoder = MemDecoder::new(&data, start_pos)?;
|
||||
|
|
@ -175,7 +174,6 @@ impl<'sess> OnDiskCache<'sess> {
|
|||
serialized_data: RwLock::new(Some(data)),
|
||||
file_index_to_stable_id: footer.file_index_to_stable_id,
|
||||
file_index_to_file: Default::default(),
|
||||
source_map: sess.source_map(),
|
||||
current_side_effects: Default::default(),
|
||||
query_result_index: footer.query_result_index.into_iter().collect(),
|
||||
prev_side_effects_index: footer.side_effects_index.into_iter().collect(),
|
||||
|
|
@ -187,12 +185,11 @@ impl<'sess> OnDiskCache<'sess> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn new_empty(source_map: &'sess SourceMap) -> Self {
|
||||
pub fn new_empty() -> Self {
|
||||
Self {
|
||||
serialized_data: RwLock::new(None),
|
||||
file_index_to_stable_id: Default::default(),
|
||||
file_index_to_file: Default::default(),
|
||||
source_map,
|
||||
current_side_effects: Default::default(),
|
||||
query_result_index: Default::default(),
|
||||
prev_side_effects_index: Default::default(),
|
||||
|
|
@ -423,7 +420,7 @@ impl<'sess> OnDiskCache<'sess> {
|
|||
}
|
||||
|
||||
fn with_decoder<'a, 'tcx, T, F: for<'s> FnOnce(&mut CacheDecoder<'s, 'tcx>) -> T>(
|
||||
&'sess self,
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
pos: AbsoluteBytePos,
|
||||
f: F,
|
||||
|
|
@ -436,7 +433,6 @@ impl<'sess> OnDiskCache<'sess> {
|
|||
tcx,
|
||||
opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize())
|
||||
.unwrap(),
|
||||
source_map: self.source_map,
|
||||
file_index_to_file: &self.file_index_to_file,
|
||||
file_index_to_stable_id: &self.file_index_to_stable_id,
|
||||
alloc_decoding_session: self.alloc_decoding_state.new_decoding_session(),
|
||||
|
|
@ -457,7 +453,6 @@ impl<'sess> OnDiskCache<'sess> {
|
|||
pub struct CacheDecoder<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
opaque: MemDecoder<'a>,
|
||||
source_map: &'a SourceMap,
|
||||
file_index_to_file: &'a Lock<FxHashMap<SourceFileIndex, Lrc<SourceFile>>>,
|
||||
file_index_to_stable_id: &'a FxHashMap<SourceFileIndex, EncodedSourceFileId>,
|
||||
alloc_decoding_session: AllocDecodingSession<'a>,
|
||||
|
|
@ -470,8 +465,7 @@ pub struct CacheDecoder<'a, 'tcx> {
|
|||
impl<'a, 'tcx> CacheDecoder<'a, 'tcx> {
|
||||
#[inline]
|
||||
fn file_index_to_file(&self, index: SourceFileIndex) -> Lrc<SourceFile> {
|
||||
let CacheDecoder { tcx, file_index_to_file, file_index_to_stable_id, source_map, .. } =
|
||||
*self;
|
||||
let CacheDecoder { tcx, file_index_to_file, file_index_to_stable_id, .. } = *self;
|
||||
|
||||
Lrc::clone(file_index_to_file.borrow_mut().entry(index).or_insert_with(|| {
|
||||
let source_file_id = &file_index_to_stable_id[&index];
|
||||
|
|
@ -490,7 +484,8 @@ impl<'a, 'tcx> CacheDecoder<'a, 'tcx> {
|
|||
self.tcx.import_source_files(source_file_cnum);
|
||||
}
|
||||
|
||||
source_map
|
||||
tcx.sess
|
||||
.source_map()
|
||||
.source_file_by_stable_id(source_file_id.stable_source_file_id)
|
||||
.expect("failed to lookup `SourceFile` in new context")
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ pub struct QuerySystem<'tcx> {
|
|||
/// Do not access this directly. It is only meant to be used by
|
||||
/// `DepGraph::try_mark_green()` and the query infrastructure.
|
||||
/// This is `None` if we are not incremental compilation mode
|
||||
pub on_disk_cache: Option<OnDiskCache<'tcx>>,
|
||||
pub on_disk_cache: Option<OnDiskCache>,
|
||||
|
||||
pub fns: QuerySystemFns<'tcx>,
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
//! Diagnostics related methods for `Ty`.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display};
|
||||
use rustc_errors::{
|
||||
Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, pluralize,
|
||||
};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind};
|
||||
|
|
@ -161,7 +162,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>(
|
|||
true
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum SuggestChangingConstraintsMessage<'a> {
|
||||
RestrictBoundFurther,
|
||||
RestrictType { ty: &'a str },
|
||||
|
|
@ -172,7 +173,7 @@ enum SuggestChangingConstraintsMessage<'a> {
|
|||
|
||||
fn suggest_changing_unsized_bound(
|
||||
generics: &hir::Generics<'_>,
|
||||
suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
|
||||
suggestions: &mut Vec<(Span, String, String, SuggestChangingConstraintsMessage<'_>)>,
|
||||
param: &hir::GenericParam<'_>,
|
||||
def_id: Option<DefId>,
|
||||
) {
|
||||
|
|
@ -207,7 +208,8 @@ fn suggest_changing_unsized_bound(
|
|||
continue;
|
||||
}
|
||||
|
||||
let mut push_suggestion = |sp, msg| suggestions.push((sp, String::new(), msg));
|
||||
let mut push_suggestion =
|
||||
|sp, msg| suggestions.push((sp, "Sized".to_string(), String::new(), msg));
|
||||
|
||||
if predicate.bounds.len() == unsized_bounds.len() {
|
||||
// All the bounds are unsized bounds, e.g.
|
||||
|
|
@ -278,8 +280,25 @@ pub fn suggest_constraining_type_params<'a>(
|
|||
span_to_replace: Option<Span>,
|
||||
) -> bool {
|
||||
let mut grouped = FxHashMap::default();
|
||||
let mut unstable_suggestion = false;
|
||||
param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
|
||||
grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id))
|
||||
let stable = match def_id {
|
||||
Some(def_id) => match tcx.lookup_stability(def_id) {
|
||||
Some(s) => s.level.is_stable(),
|
||||
None => true,
|
||||
},
|
||||
None => true,
|
||||
};
|
||||
if stable || tcx.sess.is_nightly_build() {
|
||||
grouped.entry(param_name).or_insert(Vec::new()).push((
|
||||
constraint,
|
||||
def_id,
|
||||
if stable { "" } else { "unstable " },
|
||||
));
|
||||
if !stable {
|
||||
unstable_suggestion = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
|
@ -290,16 +309,21 @@ pub fn suggest_constraining_type_params<'a>(
|
|||
let Some(param) = param else { return false };
|
||||
|
||||
{
|
||||
let mut sized_constraints = constraints.extract_if(|(_, def_id)| {
|
||||
let mut sized_constraints = constraints.extract_if(|(_, def_id, _)| {
|
||||
def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))
|
||||
});
|
||||
if let Some((_, def_id)) = sized_constraints.next() {
|
||||
if let Some((_, def_id, _)) = sized_constraints.next() {
|
||||
applicability = Applicability::MaybeIncorrect;
|
||||
|
||||
err.span_label(param.span, "this type parameter needs to be `Sized`");
|
||||
suggest_changing_unsized_bound(generics, &mut suggestions, param, def_id);
|
||||
}
|
||||
}
|
||||
let bound_message = if constraints.iter().any(|(_, def_id, _)| def_id.is_none()) {
|
||||
SuggestChangingConstraintsMessage::RestrictBoundFurther
|
||||
} else {
|
||||
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }
|
||||
};
|
||||
|
||||
// in the scenario like impl has stricter requirements than trait,
|
||||
// we should not suggest restrict bound on the impl, here we double check
|
||||
|
|
@ -312,15 +336,54 @@ pub fn suggest_constraining_type_params<'a>(
|
|||
.collect();
|
||||
|
||||
constraints
|
||||
.retain(|(_, def_id)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def)));
|
||||
.retain(|(_, def_id, _)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def)));
|
||||
|
||||
if constraints.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>();
|
||||
let mut constraint = constraints.iter().map(|&(c, _, _)| c).collect::<Vec<_>>();
|
||||
constraint.sort();
|
||||
constraint.dedup();
|
||||
let all_known = constraints.iter().all(|&(_, def_id, _)| def_id.is_some());
|
||||
let all_stable = constraints.iter().all(|&(_, _, stable)| stable.is_empty());
|
||||
let all_unstable = constraints.iter().all(|&(_, _, stable)| !stable.is_empty());
|
||||
let post = if all_stable || all_unstable {
|
||||
// Don't redundantly say "trait `X`, trait `Y`", instead "traits `X` and `Y`"
|
||||
let mut trait_names = constraints
|
||||
.iter()
|
||||
.map(|&(c, def_id, _)| match def_id {
|
||||
None => format!("`{c}`"),
|
||||
Some(def_id) => format!("`{}`", tcx.item_name(def_id)),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
trait_names.sort();
|
||||
trait_names.dedup();
|
||||
let n = trait_names.len();
|
||||
let stable = if all_stable { "" } else { "unstable " };
|
||||
let trait_ = if all_known { format!("trait{}", pluralize!(n)) } else { String::new() };
|
||||
format!("{stable}{trait_}{}", match &trait_names[..] {
|
||||
[t] => format!(" {t}"),
|
||||
[ts @ .., last] => format!(" {} and {last}", ts.join(", ")),
|
||||
[] => return false,
|
||||
},)
|
||||
} else {
|
||||
// We're more explicit when there's a mix of stable and unstable traits.
|
||||
let mut trait_names = constraints
|
||||
.iter()
|
||||
.map(|&(c, def_id, stable)| match def_id {
|
||||
None => format!("`{c}`"),
|
||||
Some(def_id) => format!("{stable}trait `{}`", tcx.item_name(def_id)),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
trait_names.sort();
|
||||
trait_names.dedup();
|
||||
match &trait_names[..] {
|
||||
[t] => t.to_string(),
|
||||
[ts @ .., last] => format!("{} and {last}", ts.join(", ")),
|
||||
[] => return false,
|
||||
}
|
||||
};
|
||||
let constraint = constraint.join(" + ");
|
||||
let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| {
|
||||
let suggestion = if span_to_replace.is_some() {
|
||||
|
|
@ -333,13 +396,11 @@ pub fn suggest_constraining_type_params<'a>(
|
|||
format!(" {constraint}")
|
||||
};
|
||||
|
||||
use SuggestChangingConstraintsMessage::RestrictBoundFurther;
|
||||
|
||||
if let Some(open_paren_sp) = open_paren_sp {
|
||||
suggestions.push((open_paren_sp, "(".to_string(), RestrictBoundFurther));
|
||||
suggestions.push((span, format!("){suggestion}"), RestrictBoundFurther));
|
||||
suggestions.push((open_paren_sp, post.clone(), "(".to_string(), bound_message));
|
||||
suggestions.push((span, post.clone(), format!("){suggestion}"), bound_message));
|
||||
} else {
|
||||
suggestions.push((span, suggestion, RestrictBoundFurther));
|
||||
suggestions.push((span, post.clone(), suggestion, bound_message));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -397,7 +458,8 @@ pub fn suggest_constraining_type_params<'a>(
|
|||
// - insert: `, X: Bar`
|
||||
suggestions.push((
|
||||
generics.tail_span_for_predicate_suggestion(),
|
||||
constraints.iter().fold(String::new(), |mut string, &(constraint, _)| {
|
||||
post,
|
||||
constraints.iter().fold(String::new(), |mut string, &(constraint, _, _)| {
|
||||
write!(string, ", {param_name}: {constraint}").unwrap();
|
||||
string
|
||||
}),
|
||||
|
|
@ -426,6 +488,7 @@ pub fn suggest_constraining_type_params<'a>(
|
|||
// default (`<T=Foo>`), so we suggest adding `where T: Bar`.
|
||||
suggestions.push((
|
||||
generics.tail_span_for_predicate_suggestion(),
|
||||
post,
|
||||
format!("{where_prefix} {param_name}: {constraint}"),
|
||||
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
|
||||
));
|
||||
|
|
@ -439,6 +502,7 @@ pub fn suggest_constraining_type_params<'a>(
|
|||
if let Some(colon_span) = param.colon_span {
|
||||
suggestions.push((
|
||||
colon_span.shrink_to_hi(),
|
||||
post,
|
||||
format!(" {constraint}"),
|
||||
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
|
||||
));
|
||||
|
|
@ -451,6 +515,7 @@ pub fn suggest_constraining_type_params<'a>(
|
|||
// - help: consider restricting this type parameter with `T: Foo`
|
||||
suggestions.push((
|
||||
param.span.shrink_to_hi(),
|
||||
post,
|
||||
format!(": {constraint}"),
|
||||
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
|
||||
));
|
||||
|
|
@ -459,39 +524,46 @@ pub fn suggest_constraining_type_params<'a>(
|
|||
// FIXME: remove the suggestions that are from derive, as the span is not correct
|
||||
suggestions = suggestions
|
||||
.into_iter()
|
||||
.filter(|(span, _, _)| !span.in_derive_expansion())
|
||||
.filter(|(span, _, _, _)| !span.in_derive_expansion())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let suggested = !suggestions.is_empty();
|
||||
if suggestions.len() == 1 {
|
||||
let (span, suggestion, msg) = suggestions.pop().unwrap();
|
||||
let (span, post, suggestion, msg) = suggestions.pop().unwrap();
|
||||
let msg = match msg {
|
||||
SuggestChangingConstraintsMessage::RestrictBoundFurther => {
|
||||
Cow::from("consider further restricting this bound")
|
||||
format!("consider further restricting this bound")
|
||||
}
|
||||
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty }
|
||||
| SuggestChangingConstraintsMessage::RestrictType { ty }
|
||||
if ty.starts_with("impl ") =>
|
||||
{
|
||||
format!("consider restricting opaque type `{ty}` with {post}")
|
||||
}
|
||||
SuggestChangingConstraintsMessage::RestrictType { ty } => {
|
||||
Cow::from(format!("consider restricting type parameter `{ty}`"))
|
||||
format!("consider restricting type parameter `{ty}` with {post}")
|
||||
}
|
||||
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
|
||||
Cow::from(format!("consider further restricting type parameter `{ty}`"))
|
||||
format!("consider further restricting type parameter `{ty}` with {post}")
|
||||
}
|
||||
SuggestChangingConstraintsMessage::RemoveMaybeUnsized => {
|
||||
Cow::from("consider removing the `?Sized` bound to make the type parameter `Sized`")
|
||||
format!("consider removing the `?Sized` bound to make the type parameter `Sized`")
|
||||
}
|
||||
SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized => {
|
||||
Cow::from("consider replacing `?Sized` with `Sized`")
|
||||
format!("consider replacing `?Sized` with `Sized`")
|
||||
}
|
||||
};
|
||||
|
||||
err.span_suggestion_verbose(span, msg, suggestion, applicability);
|
||||
} else if suggestions.len() > 1 {
|
||||
let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" };
|
||||
err.multipart_suggestion_verbose(
|
||||
"consider restricting type parameters",
|
||||
suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(),
|
||||
format!("consider restricting type parameters{post}"),
|
||||
suggestions.into_iter().map(|(span, _, suggestion, _)| (span, suggestion)).collect(),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
true
|
||||
suggested
|
||||
}
|
||||
|
||||
/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
|
||||
|
|
|
|||
|
|
@ -2,19 +2,186 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::OnceLock;
|
||||
use std::{io, ops, str};
|
||||
|
||||
use regex::Regex;
|
||||
use rustc_graphviz as dot;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::{self, BasicBlock, Body, Location, graphviz_safe_def_name};
|
||||
use rustc_middle::mir::{
|
||||
self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name,
|
||||
traversal,
|
||||
};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_span::symbol::{Symbol, sym};
|
||||
use tracing::debug;
|
||||
use {rustc_ast as ast, rustc_graphviz as dot};
|
||||
|
||||
use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
|
||||
use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor};
|
||||
use crate::errors::{
|
||||
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
|
||||
};
|
||||
|
||||
/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
|
||||
/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are
|
||||
/// the same.
|
||||
pub(super) fn write_graphviz_results<'tcx, A>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
results: &mut Results<'tcx, A>,
|
||||
pass_name: Option<&'static str>,
|
||||
) -> std::io::Result<()>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
A::Domain: DebugWithContext<A>,
|
||||
{
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
let def_id = body.source.def_id();
|
||||
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
|
||||
// Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let file = try {
|
||||
match attrs.output_path(A::NAME) {
|
||||
Some(path) => {
|
||||
debug!("printing dataflow results for {:?} to {}", def_id, path.display());
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
fs::File::create_buffered(&path)?
|
||||
}
|
||||
|
||||
None if dump_enabled(tcx, A::NAME, def_id) => {
|
||||
create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
|
||||
}
|
||||
|
||||
_ => return Ok(()),
|
||||
}
|
||||
};
|
||||
let mut file = match file {
|
||||
Ok(f) => f,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let style = match attrs.formatter {
|
||||
Some(sym::two_phase) => OutputStyle::BeforeAndAfter,
|
||||
_ => OutputStyle::AfterOnly,
|
||||
};
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
let graphviz = Formatter::new(body, results, style);
|
||||
let mut render_opts =
|
||||
vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())];
|
||||
if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
|
||||
render_opts.push(dot::RenderOption::DarkTheme);
|
||||
}
|
||||
let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts));
|
||||
|
||||
let lhs = try {
|
||||
r?;
|
||||
file.write_all(&buf)?;
|
||||
};
|
||||
|
||||
lhs
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RustcMirAttrs {
|
||||
basename_and_suffix: Option<PathBuf>,
|
||||
formatter: Option<Symbol>,
|
||||
}
|
||||
|
||||
impl RustcMirAttrs {
|
||||
fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> {
|
||||
let mut result = Ok(());
|
||||
let mut ret = RustcMirAttrs::default();
|
||||
|
||||
let rustc_mir_attrs = tcx
|
||||
.get_attrs(def_id, sym::rustc_mir)
|
||||
.flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
|
||||
|
||||
for attr in rustc_mir_attrs {
|
||||
let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
|
||||
Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
|
||||
let path = PathBuf::from(s.to_string());
|
||||
match path.file_name() {
|
||||
Some(_) => Ok(path),
|
||||
None => {
|
||||
tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() });
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if attr.has_name(sym::borrowck_graphviz_format) {
|
||||
Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
|
||||
sym::gen_kill | sym::two_phase => Ok(s),
|
||||
_ => {
|
||||
tcx.dcx().emit_err(UnknownFormatter { span: attr.span() });
|
||||
Err(())
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
result = result.and(attr_result);
|
||||
}
|
||||
|
||||
result.map(|()| ret)
|
||||
}
|
||||
|
||||
fn set_field<T>(
|
||||
field: &mut Option<T>,
|
||||
tcx: TyCtxt<'_>,
|
||||
attr: &ast::MetaItemInner,
|
||||
mapper: impl FnOnce(Symbol) -> Result<T, ()>,
|
||||
) -> Result<(), ()> {
|
||||
if field.is_some() {
|
||||
tcx.dcx()
|
||||
.emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() });
|
||||
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if let Some(s) = attr.value_str() {
|
||||
*field = Some(mapper(s)?);
|
||||
Ok(())
|
||||
} else {
|
||||
tcx.dcx()
|
||||
.emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() });
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path where dataflow results should be written, or `None`
|
||||
/// `borrowck_graphviz_postflow` was not specified.
|
||||
///
|
||||
/// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
|
||||
///
|
||||
/// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
|
||||
fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
|
||||
let mut ret = self.basename_and_suffix.as_ref().cloned()?;
|
||||
let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
|
||||
|
||||
let mut file_name: OsString = analysis_name.into();
|
||||
file_name.push("_");
|
||||
file_name.push(suffix);
|
||||
ret.set_file_name(file_name);
|
||||
|
||||
Some(ret)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum OutputStyle {
|
||||
enum OutputStyle {
|
||||
AfterOnly,
|
||||
BeforeAndAfter,
|
||||
}
|
||||
|
|
@ -28,7 +195,7 @@ impl OutputStyle {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Formatter<'mir, 'tcx, A>
|
||||
struct Formatter<'mir, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
|
|
@ -45,12 +212,12 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A>
|
|||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
fn new(
|
||||
body: &'mir Body<'tcx>,
|
||||
results: &'mir mut Results<'tcx, A>,
|
||||
style: OutputStyle,
|
||||
) -> Self {
|
||||
let reachable = mir::traversal::reachable_as_bitset(body);
|
||||
let reachable = traversal::reachable_as_bitset(body);
|
||||
Formatter { cursor: results.as_results_cursor(body).into(), style, reachable }
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +228,7 @@ where
|
|||
|
||||
/// A pair of a basic block and an index into that basic blocks `successors`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub(crate) struct CfgEdge {
|
||||
struct CfgEdge {
|
||||
source: BasicBlock,
|
||||
index: usize,
|
||||
}
|
||||
|
|
@ -520,7 +687,7 @@ struct StateDiffCollector<D> {
|
|||
|
||||
impl<D> StateDiffCollector<D> {
|
||||
fn run<'tcx, A>(
|
||||
body: &mir::Body<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
block: BasicBlock,
|
||||
results: &mut Results<'tcx, A>,
|
||||
style: OutputStyle,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, Terminator
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use tracing::error;
|
||||
|
||||
use self::results::write_graphviz_results;
|
||||
use self::graphviz::write_graphviz_results;
|
||||
use super::fmt::DebugWithContext;
|
||||
|
||||
mod cursor;
|
||||
|
|
|
|||
|
|
@ -1,22 +1,9 @@
|
|||
//! Dataflow analysis results.
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::{self, BasicBlock, create_dump_file, dump_enabled, traversal};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_span::symbol::{Symbol, sym};
|
||||
use tracing::debug;
|
||||
use {rustc_ast as ast, rustc_graphviz as dot};
|
||||
use rustc_middle::mir::{BasicBlock, Body, traversal};
|
||||
|
||||
use super::fmt::DebugWithContext;
|
||||
use super::{Analysis, ResultsCursor, ResultsVisitor, graphviz, visit_results};
|
||||
use crate::errors::{
|
||||
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
|
||||
};
|
||||
use super::{Analysis, ResultsCursor, ResultsVisitor, visit_results};
|
||||
use crate::framework::cursor::ResultsHandle;
|
||||
|
||||
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
|
||||
|
|
@ -41,16 +28,13 @@ where
|
|||
/// `Results` is also used outside the cursor.
|
||||
pub fn as_results_cursor<'mir>(
|
||||
&'mir mut self,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
body: &'mir Body<'tcx>,
|
||||
) -> ResultsCursor<'mir, 'tcx, A> {
|
||||
ResultsCursor::new(body, ResultsHandle::BorrowedMut(self))
|
||||
}
|
||||
|
||||
/// Creates a `ResultsCursor` that takes ownership of the `Results`.
|
||||
pub fn into_results_cursor<'mir>(
|
||||
self,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
) -> ResultsCursor<'mir, 'tcx, A> {
|
||||
pub fn into_results_cursor<'mir>(self, body: &'mir Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
|
||||
ResultsCursor::new(body, ResultsHandle::Owned(self))
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +45,7 @@ where
|
|||
|
||||
pub fn visit_with<'mir>(
|
||||
&mut self,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
body: &'mir Body<'tcx>,
|
||||
blocks: impl IntoIterator<Item = BasicBlock>,
|
||||
vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
|
||||
) {
|
||||
|
|
@ -70,166 +54,10 @@ where
|
|||
|
||||
pub fn visit_reachable_with<'mir>(
|
||||
&mut self,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
body: &'mir Body<'tcx>,
|
||||
vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
|
||||
) {
|
||||
let blocks = traversal::reachable(body);
|
||||
visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
|
||||
}
|
||||
}
|
||||
|
||||
// Graphviz
|
||||
|
||||
/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
|
||||
/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are
|
||||
/// the same.
|
||||
pub(super) fn write_graphviz_results<'tcx, A>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &mir::Body<'tcx>,
|
||||
results: &mut Results<'tcx, A>,
|
||||
pass_name: Option<&'static str>,
|
||||
) -> std::io::Result<()>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
A::Domain: DebugWithContext<A>,
|
||||
{
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
let def_id = body.source.def_id();
|
||||
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
|
||||
// Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let file = try {
|
||||
match attrs.output_path(A::NAME) {
|
||||
Some(path) => {
|
||||
debug!("printing dataflow results for {:?} to {}", def_id, path.display());
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
fs::File::create_buffered(&path)?
|
||||
}
|
||||
|
||||
None if dump_enabled(tcx, A::NAME, def_id) => {
|
||||
create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
|
||||
}
|
||||
|
||||
_ => return Ok(()),
|
||||
}
|
||||
};
|
||||
let mut file = match file {
|
||||
Ok(f) => f,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let style = match attrs.formatter {
|
||||
Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
|
||||
_ => graphviz::OutputStyle::AfterOnly,
|
||||
};
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
let graphviz = graphviz::Formatter::new(body, results, style);
|
||||
let mut render_opts =
|
||||
vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())];
|
||||
if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
|
||||
render_opts.push(dot::RenderOption::DarkTheme);
|
||||
}
|
||||
let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts));
|
||||
|
||||
let lhs = try {
|
||||
r?;
|
||||
file.write_all(&buf)?;
|
||||
};
|
||||
|
||||
lhs
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RustcMirAttrs {
|
||||
basename_and_suffix: Option<PathBuf>,
|
||||
formatter: Option<Symbol>,
|
||||
}
|
||||
|
||||
impl RustcMirAttrs {
|
||||
fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> {
|
||||
let mut result = Ok(());
|
||||
let mut ret = RustcMirAttrs::default();
|
||||
|
||||
let rustc_mir_attrs = tcx
|
||||
.get_attrs(def_id, sym::rustc_mir)
|
||||
.flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
|
||||
|
||||
for attr in rustc_mir_attrs {
|
||||
let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
|
||||
Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
|
||||
let path = PathBuf::from(s.to_string());
|
||||
match path.file_name() {
|
||||
Some(_) => Ok(path),
|
||||
None => {
|
||||
tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() });
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if attr.has_name(sym::borrowck_graphviz_format) {
|
||||
Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
|
||||
sym::gen_kill | sym::two_phase => Ok(s),
|
||||
_ => {
|
||||
tcx.dcx().emit_err(UnknownFormatter { span: attr.span() });
|
||||
Err(())
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
result = result.and(attr_result);
|
||||
}
|
||||
|
||||
result.map(|()| ret)
|
||||
}
|
||||
|
||||
fn set_field<T>(
|
||||
field: &mut Option<T>,
|
||||
tcx: TyCtxt<'_>,
|
||||
attr: &ast::MetaItemInner,
|
||||
mapper: impl FnOnce(Symbol) -> Result<T, ()>,
|
||||
) -> Result<(), ()> {
|
||||
if field.is_some() {
|
||||
tcx.dcx()
|
||||
.emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() });
|
||||
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if let Some(s) = attr.value_str() {
|
||||
*field = Some(mapper(s)?);
|
||||
Ok(())
|
||||
} else {
|
||||
tcx.dcx()
|
||||
.emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() });
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path where dataflow results should be written, or `None`
|
||||
/// `borrowck_graphviz_postflow` was not specified.
|
||||
///
|
||||
/// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
|
||||
///
|
||||
/// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
|
||||
fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
|
||||
let mut ret = self.basename_and_suffix.as_ref().cloned()?;
|
||||
let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
|
||||
|
||||
let mut file_name: OsString = analysis_name.into();
|
||||
file_name.push("_");
|
||||
file_name.push(suffix);
|
||||
ret.set_file_name(file_name);
|
||||
|
||||
Some(ret)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
//! Dataflow analyses are built upon some interpretation of the
|
||||
//! bitvectors attached to each basic block, represented via a
|
||||
//! zero-sized structure.
|
||||
|
||||
mod borrowed_locals;
|
||||
mod initialized;
|
||||
mod liveness;
|
||||
|
|
|
|||
|
|
@ -2631,7 +2631,7 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
self.bump(); // Eat `let` token
|
||||
let lo = self.prev_token.span;
|
||||
let pat = self.parse_pat_allow_top_alt(
|
||||
let pat = self.parse_pat_no_top_guard(
|
||||
None,
|
||||
RecoverComma::Yes,
|
||||
RecoverColon::Yes,
|
||||
|
|
@ -2778,7 +2778,7 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
// Try to parse the pattern `for ($PAT) in $EXPR`.
|
||||
let pat = match (
|
||||
self.parse_pat_allow_top_alt(
|
||||
self.parse_pat_allow_top_guard(
|
||||
None,
|
||||
RecoverComma::Yes,
|
||||
RecoverColon::Yes,
|
||||
|
|
@ -3241,7 +3241,7 @@ impl<'a> Parser<'a> {
|
|||
// then we should recover.
|
||||
let mut snapshot = this.create_snapshot_for_diagnostic();
|
||||
let pattern_follows = snapshot
|
||||
.parse_pat_allow_top_alt(
|
||||
.parse_pat_no_top_guard(
|
||||
None,
|
||||
RecoverComma::Yes,
|
||||
RecoverColon::Yes,
|
||||
|
|
@ -3315,43 +3315,37 @@ impl<'a> Parser<'a> {
|
|||
|
||||
fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Expr>>)> {
|
||||
if self.token == token::OpenDelim(Delimiter::Parenthesis) {
|
||||
// Detect and recover from `($pat if $cond) => $arm`.
|
||||
let left = self.token.span;
|
||||
match self.parse_pat_allow_top_alt(
|
||||
let pat = self.parse_pat_no_top_guard(
|
||||
None,
|
||||
RecoverComma::Yes,
|
||||
RecoverColon::Yes,
|
||||
CommaRecoveryMode::EitherTupleOrPipe,
|
||||
) {
|
||||
Ok(pat) => Ok((pat, self.parse_match_arm_guard()?)),
|
||||
Err(err)
|
||||
if let prev_sp = self.prev_token.span
|
||||
&& let true = self.eat_keyword(kw::If) =>
|
||||
{
|
||||
// We know for certain we've found `($pat if` so far.
|
||||
let mut cond = match self.parse_match_guard_condition() {
|
||||
Ok(cond) => cond,
|
||||
Err(cond_err) => {
|
||||
cond_err.cancel();
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
err.cancel();
|
||||
CondChecker::new(self).visit_expr(&mut cond);
|
||||
self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]);
|
||||
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
|
||||
let right = self.prev_token.span;
|
||||
self.dcx().emit_err(errors::ParenthesesInMatchPat {
|
||||
span: vec![left, right],
|
||||
sugg: errors::ParenthesesInMatchPatSugg { left, right },
|
||||
});
|
||||
Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond)))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
)?;
|
||||
if let ast::PatKind::Paren(subpat) = &pat.kind
|
||||
&& let ast::PatKind::Guard(..) = &subpat.kind
|
||||
{
|
||||
// Detect and recover from `($pat if $cond) => $arm`.
|
||||
// FIXME(guard_patterns): convert this to a normal guard instead
|
||||
let span = pat.span;
|
||||
let ast::PatKind::Paren(subpat) = pat.into_inner().kind else { unreachable!() };
|
||||
let ast::PatKind::Guard(_, mut cond) = subpat.into_inner().kind else {
|
||||
unreachable!()
|
||||
};
|
||||
self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span);
|
||||
CondChecker::new(self).visit_expr(&mut cond);
|
||||
let right = self.prev_token.span;
|
||||
self.dcx().emit_err(errors::ParenthesesInMatchPat {
|
||||
span: vec![left, right],
|
||||
sugg: errors::ParenthesesInMatchPatSugg { left, right },
|
||||
});
|
||||
Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond)))
|
||||
} else {
|
||||
Ok((pat, self.parse_match_arm_guard()?))
|
||||
}
|
||||
} else {
|
||||
// Regular parser flow:
|
||||
let pat = self.parse_pat_allow_top_alt(
|
||||
let pat = self.parse_pat_no_top_guard(
|
||||
None,
|
||||
RecoverComma::Yes,
|
||||
RecoverColon::Yes,
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ impl<'a> Parser<'a> {
|
|||
NonterminalKind::Pat(pat_kind) => {
|
||||
NtPat(self.collect_tokens_no_attrs(|this| match pat_kind {
|
||||
PatParam { .. } => this.parse_pat_no_top_alt(None, None),
|
||||
PatWithOr => this.parse_pat_allow_top_alt(
|
||||
PatWithOr => this.parse_pat_no_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
|
|
|
|||
|
|
@ -99,9 +99,34 @@ pub enum PatternLocation {
|
|||
impl<'a> Parser<'a> {
|
||||
/// Parses a pattern.
|
||||
///
|
||||
/// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns
|
||||
/// at the top level. Used when parsing the parameters of lambda expressions,
|
||||
/// functions, function pointers, and `pat` macro fragments.
|
||||
/// Corresponds to `Pattern` in RFC 3637 and admits guard patterns at the top level.
|
||||
/// Used when parsing patterns in all cases where neither `PatternNoTopGuard` nor
|
||||
/// `PatternNoTopAlt` (see below) are used.
|
||||
pub fn parse_pat_allow_top_guard(
|
||||
&mut self,
|
||||
expected: Option<Expected>,
|
||||
rc: RecoverComma,
|
||||
ra: RecoverColon,
|
||||
rt: CommaRecoveryMode,
|
||||
) -> PResult<'a, P<Pat>> {
|
||||
let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?;
|
||||
|
||||
if self.eat_keyword(kw::If) {
|
||||
let cond = self.parse_expr()?;
|
||||
// Feature-gate guard patterns
|
||||
self.psess.gated_spans.gate(sym::guard_patterns, cond.span);
|
||||
let span = pat.span.to(cond.span);
|
||||
Ok(self.mk_pat(span, PatKind::Guard(pat, cond)))
|
||||
} else {
|
||||
Ok(pat)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a pattern.
|
||||
///
|
||||
/// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns
|
||||
/// or guard patterns at the top level. Used when parsing the parameters of lambda
|
||||
/// expressions, functions, function pointers, and `pat_param` macro fragments.
|
||||
pub fn parse_pat_no_top_alt(
|
||||
&mut self,
|
||||
expected: Option<Expected>,
|
||||
|
|
@ -112,25 +137,26 @@ impl<'a> Parser<'a> {
|
|||
|
||||
/// Parses a pattern.
|
||||
///
|
||||
/// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level.
|
||||
/// Used for parsing patterns in all cases when `pat<no_top_alt>` is not used.
|
||||
/// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not
|
||||
/// guard patterns, at the top level. Used for parsing patterns in `pat` fragments (until
|
||||
/// the next edition) and `let`, `if let`, and `while let` expressions.
|
||||
///
|
||||
/// Note that after the FCP in <https://github.com/rust-lang/rust/issues/81415>,
|
||||
/// a leading vert is allowed in nested or-patterns, too. This allows us to
|
||||
/// simplify the grammar somewhat.
|
||||
pub fn parse_pat_allow_top_alt(
|
||||
pub fn parse_pat_no_top_guard(
|
||||
&mut self,
|
||||
expected: Option<Expected>,
|
||||
rc: RecoverComma,
|
||||
ra: RecoverColon,
|
||||
rt: CommaRecoveryMode,
|
||||
) -> PResult<'a, P<Pat>> {
|
||||
self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat)
|
||||
self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat)
|
||||
}
|
||||
|
||||
/// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true =
|
||||
/// recovered).
|
||||
fn parse_pat_allow_top_alt_inner(
|
||||
fn parse_pat_no_top_guard_inner(
|
||||
&mut self,
|
||||
expected: Option<Expected>,
|
||||
rc: RecoverComma,
|
||||
|
|
@ -231,7 +257,7 @@ impl<'a> Parser<'a> {
|
|||
// We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level
|
||||
// or-patterns so that we can detect when a user tries to use it. This allows us to print a
|
||||
// better error message.
|
||||
let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner(
|
||||
let (pat, trailing_vert) = self.parse_pat_no_top_guard_inner(
|
||||
expected,
|
||||
rc,
|
||||
RecoverColon::No,
|
||||
|
|
@ -696,7 +722,7 @@ impl<'a> Parser<'a> {
|
|||
} else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
|
||||
// Parse `[pat, pat,...]` as a slice pattern.
|
||||
let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| {
|
||||
p.parse_pat_allow_top_alt(
|
||||
p.parse_pat_allow_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
|
|
@ -944,7 +970,7 @@ impl<'a> Parser<'a> {
|
|||
let open_paren = self.token.span;
|
||||
|
||||
let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
|
||||
p.parse_pat_allow_top_alt(
|
||||
p.parse_pat_allow_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
|
|
@ -1361,7 +1387,7 @@ impl<'a> Parser<'a> {
|
|||
path: Path,
|
||||
) -> PResult<'a, PatKind> {
|
||||
let (fields, _) = self.parse_paren_comma_seq(|p| {
|
||||
p.parse_pat_allow_top_alt(
|
||||
p.parse_pat_allow_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
|
|
@ -1396,7 +1422,7 @@ impl<'a> Parser<'a> {
|
|||
self.parse_builtin(|self_, _lo, ident| {
|
||||
Ok(match ident.name {
|
||||
// builtin#deref(PAT)
|
||||
sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt(
|
||||
sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard(
|
||||
None,
|
||||
RecoverComma::Yes,
|
||||
RecoverColon::Yes,
|
||||
|
|
@ -1671,7 +1697,7 @@ impl<'a> Parser<'a> {
|
|||
// Parsing a pattern of the form `fieldname: pat`.
|
||||
let fieldname = self.parse_field_name()?;
|
||||
self.bump();
|
||||
let pat = self.parse_pat_allow_top_alt(
|
||||
let pat = self.parse_pat_allow_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
|
|
|
|||
|
|
@ -469,7 +469,7 @@ impl<'a> Parser<'a> {
|
|||
PathStyle::Pat
|
||||
if let Ok(_) = self
|
||||
.parse_paren_comma_seq(|p| {
|
||||
p.parse_pat_allow_top_alt(
|
||||
p.parse_pat_allow_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
|
|
|
|||
|
|
@ -555,6 +555,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
|
|||
Slice,
|
||||
Rest,
|
||||
Never,
|
||||
Guard,
|
||||
Paren,
|
||||
MacCall,
|
||||
Err
|
||||
|
|
|
|||
|
|
@ -198,12 +198,12 @@ trait QueryConfigRestored<'tcx> {
|
|||
-> Self::RestoredValue;
|
||||
}
|
||||
|
||||
pub fn query_system<'tcx>(
|
||||
pub fn query_system<'a>(
|
||||
local_providers: Providers,
|
||||
extern_providers: ExternProviders,
|
||||
on_disk_cache: Option<OnDiskCache<'tcx>>,
|
||||
on_disk_cache: Option<OnDiskCache>,
|
||||
incremental: bool,
|
||||
) -> QuerySystem<'tcx> {
|
||||
) -> QuerySystem<'a> {
|
||||
QuerySystem {
|
||||
states: Default::default(),
|
||||
arenas: Default::default(),
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ use rustc_errors::emitter::{
|
|||
DynEmitter, HumanEmitter, HumanReadableErrorType, OutputTheme, stderr_destination,
|
||||
};
|
||||
use rustc_errors::json::JsonEmitter;
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::{
|
||||
Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort,
|
||||
FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle,
|
||||
|
|
@ -276,11 +275,11 @@ impl Session {
|
|||
}
|
||||
|
||||
/// Invoked all the way at the end to finish off diagnostics printing.
|
||||
pub fn finish_diagnostics(&self, registry: &Registry) -> Option<ErrorGuaranteed> {
|
||||
pub fn finish_diagnostics(&self) -> Option<ErrorGuaranteed> {
|
||||
let mut guar = None;
|
||||
guar = guar.or(self.check_miri_unleashed_features());
|
||||
guar = guar.or(self.dcx().emit_stashed_diagnostics());
|
||||
self.dcx().print_error_count(registry);
|
||||
self.dcx().print_error_count();
|
||||
if self.opts.json_future_incompat {
|
||||
self.dcx().emit_future_breakage_report();
|
||||
}
|
||||
|
|
@ -880,7 +879,6 @@ impl Session {
|
|||
#[allow(rustc::bad_opt_access)]
|
||||
fn default_emitter(
|
||||
sopts: &config::Options,
|
||||
registry: rustc_errors::registry::Registry,
|
||||
source_map: Lrc<SourceMap>,
|
||||
bundle: Option<Lrc<FluentBundle>>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
|
|
@ -943,7 +941,6 @@ fn default_emitter(
|
|||
json_rendered,
|
||||
color_config,
|
||||
)
|
||||
.registry(Some(registry))
|
||||
.fluent_bundle(bundle)
|
||||
.ui_testing(sopts.unstable_opts.ui_testing)
|
||||
.ignored_directories_in_source_blocks(
|
||||
|
|
@ -999,11 +996,11 @@ pub fn build_session(
|
|||
sopts.unstable_opts.translate_directionality_markers,
|
||||
);
|
||||
let source_map = rustc_span::source_map::get_source_map().unwrap();
|
||||
let emitter =
|
||||
default_emitter(&sopts, registry, Lrc::clone(&source_map), bundle, fallback_bundle);
|
||||
let emitter = default_emitter(&sopts, Lrc::clone(&source_map), bundle, fallback_bundle);
|
||||
|
||||
let mut dcx =
|
||||
DiagCtxt::new(emitter).with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings));
|
||||
let mut dcx = DiagCtxt::new(emitter)
|
||||
.with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings))
|
||||
.with_registry(registry);
|
||||
if let Some(ice_file) = ice_file {
|
||||
dcx = dcx.with_ice_file(ice_file);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -342,8 +342,9 @@ macro_rules! run_driver {
|
|||
|
||||
/// Runs the compiler against given target and tests it with `test_function`
|
||||
pub fn run(&mut self) -> Result<C, CompilerError<B>> {
|
||||
let compiler_result = rustc_driver::catch_fatal_errors(|| {
|
||||
RunCompiler::new(&self.args.clone(), self).run()
|
||||
let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> {
|
||||
RunCompiler::new(&self.args.clone(), self).run();
|
||||
Ok(())
|
||||
});
|
||||
match (compiler_result, self.result.take()) {
|
||||
(Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value),
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ pub mod source_map;
|
|||
use source_map::{SourceMap, SourceMapInputs};
|
||||
|
||||
pub use self::caching_source_map_view::CachingSourceMapView;
|
||||
use crate::fatal_error::FatalError;
|
||||
|
||||
pub mod edition;
|
||||
use edition::Edition;
|
||||
|
|
@ -2614,6 +2615,10 @@ impl ErrorGuaranteed {
|
|||
pub fn unchecked_error_guaranteed() -> Self {
|
||||
ErrorGuaranteed(())
|
||||
}
|
||||
|
||||
pub fn raise_fatal(self) -> ! {
|
||||
FatalError.raise()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: rustc_serialize::Encoder> Encodable<E> for ErrorGuaranteed {
|
||||
|
|
|
|||
|
|
@ -999,6 +999,7 @@ symbols! {
|
|||
global_registration,
|
||||
globs,
|
||||
gt,
|
||||
guard_patterns,
|
||||
half_open_range_patterns,
|
||||
half_open_range_patterns_in_slices,
|
||||
hash,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ use std::{cmp, fmt, iter};
|
|||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize};
|
||||
use rustc_errors::{
|
||||
Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize,
|
||||
};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
|
|
@ -67,6 +69,7 @@ use rustc_middle::ty::{
|
|||
self, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
|
||||
TypeVisitableExt,
|
||||
};
|
||||
use rustc_span::def_id::LOCAL_CRATE;
|
||||
use rustc_span::{BytePos, DesugaringKind, Pos, Span, sym};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
|
|
@ -211,7 +214,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Adds a note if the types come from similarly named crates
|
||||
fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) {
|
||||
fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) -> bool {
|
||||
// FIXME(estebank): unify with `report_similar_impl_candidates`. The message is similar,
|
||||
// even if the logic needed to detect the case is very different.
|
||||
use hir::def_id::CrateNum;
|
||||
use rustc_hir::definitions::DisambiguatedDefPathData;
|
||||
use ty::GenericArg;
|
||||
|
|
@ -285,7 +290,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId| {
|
||||
let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId, ty: &str| -> bool {
|
||||
// Only report definitions from different crates. If both definitions
|
||||
// are from a local module we could have false positives, e.g.
|
||||
// let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
|
||||
|
|
@ -297,24 +302,112 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
|
||||
// We compare strings because DefPath can be different
|
||||
// for imported and non-imported crates
|
||||
let expected_str = self.tcx.def_path_str(did1);
|
||||
let found_str = self.tcx.def_path_str(did2);
|
||||
let Ok(expected_abs) = abs_path(did1) else { return false };
|
||||
let Ok(found_abs) = abs_path(did2) else { return false };
|
||||
let same_path = || -> Result<_, PrintError> {
|
||||
Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2)
|
||||
|| abs_path(did1)? == abs_path(did2)?)
|
||||
Ok(expected_str == found_str || expected_abs == found_abs)
|
||||
};
|
||||
// We want to use as unique a type path as possible. If both types are "locally
|
||||
// known" by the same name, we use the "absolute path" which uses the original
|
||||
// crate name instead.
|
||||
let (expected, found) = if expected_str == found_str {
|
||||
(expected_abs.join("::"), found_abs.join("::"))
|
||||
} else {
|
||||
(expected_str.clone(), found_str.clone())
|
||||
};
|
||||
if same_path().unwrap_or(false) {
|
||||
let crate_name = self.tcx.crate_name(did1.krate);
|
||||
let msg = if did1.is_local() || did2.is_local() {
|
||||
// We've displayed "expected `a::b`, found `a::b`". We add context to
|
||||
// differentiate the different cases where that might happen.
|
||||
let expected_crate_name = self.tcx.crate_name(did1.krate);
|
||||
let found_crate_name = self.tcx.crate_name(did2.krate);
|
||||
let same_crate = expected_crate_name == found_crate_name;
|
||||
let expected_sp = self.tcx.def_span(did1);
|
||||
let found_sp = self.tcx.def_span(did2);
|
||||
|
||||
let both_direct_dependencies = if !did1.is_local()
|
||||
&& !did2.is_local()
|
||||
&& let Some(data1) = self.tcx.extern_crate(did1.krate)
|
||||
&& let Some(data2) = self.tcx.extern_crate(did2.krate)
|
||||
&& data1.dependency_of == LOCAL_CRATE
|
||||
&& data2.dependency_of == LOCAL_CRATE
|
||||
{
|
||||
// If both crates are directly depended on, we don't want to mention that
|
||||
// in the final message, as it is redundant wording.
|
||||
// We skip the case of semver trick, where one version of the local crate
|
||||
// depends on another version of itself by checking that both crates at play
|
||||
// are not the current one.
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut span: MultiSpan = vec![expected_sp, found_sp].into();
|
||||
span.push_span_label(
|
||||
self.tcx.def_span(did1),
|
||||
format!("this is the expected {ty} `{expected}`"),
|
||||
);
|
||||
span.push_span_label(
|
||||
self.tcx.def_span(did2),
|
||||
format!("this is the found {ty} `{found}`"),
|
||||
);
|
||||
for def_id in [did1, did2] {
|
||||
let crate_name = self.tcx.crate_name(def_id.krate);
|
||||
if !def_id.is_local()
|
||||
&& let Some(data) = self.tcx.extern_crate(def_id.krate)
|
||||
{
|
||||
let descr = if same_crate {
|
||||
"one version of".to_string()
|
||||
} else {
|
||||
format!("one {ty} comes from")
|
||||
};
|
||||
let dependency = if both_direct_dependencies {
|
||||
if let rustc_session::cstore::ExternCrateSource::Extern(def_id) =
|
||||
data.src
|
||||
&& let Some(name) = self.tcx.opt_item_name(def_id)
|
||||
{
|
||||
format!(", which is renamed locally to `{name}`")
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
} else if data.dependency_of == LOCAL_CRATE {
|
||||
", as a direct dependency of the current crate".to_string()
|
||||
} else {
|
||||
let dep = self.tcx.crate_name(data.dependency_of);
|
||||
format!(", as a dependency of crate `{dep}`")
|
||||
};
|
||||
span.push_span_label(
|
||||
data.span,
|
||||
format!("{descr} crate `{crate_name}` used here{dependency}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
let msg = if (did1.is_local() || did2.is_local()) && same_crate {
|
||||
format!(
|
||||
"the crate `{crate_name}` is compiled multiple times, possibly with different configurations"
|
||||
"the crate `{expected_crate_name}` is compiled multiple times, \
|
||||
possibly with different configurations",
|
||||
)
|
||||
} else if same_crate {
|
||||
format!(
|
||||
"two different versions of crate `{expected_crate_name}` are being \
|
||||
used; two types coming from two different versions of the same crate \
|
||||
are different types even if they look the same",
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"perhaps two different versions of crate `{crate_name}` are being used?"
|
||||
"two types coming from two different crates are different types even \
|
||||
if they look the same",
|
||||
)
|
||||
};
|
||||
err.note(msg);
|
||||
err.span_note(span, msg);
|
||||
if same_crate {
|
||||
err.help("you can use `cargo tree` to explore your dependency tree");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
match terr {
|
||||
TypeError::Sorts(ref exp_found) => {
|
||||
|
|
@ -323,14 +416,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) =
|
||||
(exp_found.expected.kind(), exp_found.found.kind())
|
||||
{
|
||||
report_path_match(err, exp_adt.did(), found_adt.did());
|
||||
return report_path_match(err, exp_adt.did(), found_adt.did(), "type");
|
||||
}
|
||||
}
|
||||
TypeError::Traits(ref exp_found) => {
|
||||
report_path_match(err, exp_found.expected, exp_found.found);
|
||||
return report_path_match(err, exp_found.expected, exp_found.found, "trait");
|
||||
}
|
||||
_ => (), // FIXME(#22750) handle traits and stuff
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn note_error_origin(
|
||||
|
|
@ -1409,6 +1503,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
label_or_note(span, terr.to_string(self.tcx));
|
||||
}
|
||||
|
||||
if self.check_and_note_conflicting_crates(diag, terr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((expected, found, path)) = expected_found {
|
||||
let (expected_label, found_label, exp_found) = match exp_found {
|
||||
Mismatch::Variable(ef) => (
|
||||
|
|
@ -1470,15 +1568,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
|prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, diag: &mut Diag<'_>| {
|
||||
let name = shadow.sort_string(self.tcx);
|
||||
diag.note(format!(
|
||||
"{prim} and {name} have similar names, but are actually distinct types"
|
||||
"`{prim}` and {name} have similar names, but are actually distinct types"
|
||||
));
|
||||
diag.note(format!(
|
||||
"one `{prim}` is a primitive defined by the language",
|
||||
));
|
||||
diag.note(format!("{prim} is a primitive defined by the language"));
|
||||
let def_span = self.tcx.def_span(defid);
|
||||
let msg = if defid.is_local() {
|
||||
format!("{name} is defined in the current crate")
|
||||
format!("the other {name} is defined in the current crate")
|
||||
} else {
|
||||
let crate_name = self.tcx.crate_name(defid.krate);
|
||||
format!("{name} is defined in crate `{crate_name}`")
|
||||
format!("the other {name} is defined in crate `{crate_name}`")
|
||||
};
|
||||
diag.span_note(def_span, msg);
|
||||
};
|
||||
|
|
@ -1666,8 +1766,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
self.check_and_note_conflicting_crates(diag, terr);
|
||||
|
||||
self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id());
|
||||
if let Some(exp_found) = exp_found
|
||||
&& let exp_found = TypeError::Sorts(exp_found)
|
||||
|
|
|
|||
|
|
@ -1745,9 +1745,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
};
|
||||
(
|
||||
data.span,
|
||||
format!(
|
||||
"one version of crate `{crate_name}` is used here, as a {dependency}"
|
||||
),
|
||||
format!("one version of crate `{crate_name}` used here, as a {dependency}"),
|
||||
)
|
||||
})
|
||||
{
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ impl bool {
|
|||
/// // `then`.
|
||||
/// assert_eq!(a, 1);
|
||||
/// ```
|
||||
#[doc(alias = "then_with")]
|
||||
#[stable(feature = "lazy_bool_to_option", since = "1.50.0")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "bool_then")]
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -3577,34 +3577,44 @@ pub const fn discriminant_value<T>(_v: &T) -> <T as DiscriminantKind>::Discrimin
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
extern "rust-intrinsic" {
|
||||
/// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the
|
||||
/// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs.
|
||||
///
|
||||
/// `catch_fn` must not unwind.
|
||||
///
|
||||
/// The third argument is a function called if an unwind occurs (both Rust `panic` and foreign
|
||||
/// unwinds). This function takes the data pointer and a pointer to the target- and
|
||||
/// runtime-specific exception object that was caught.
|
||||
///
|
||||
/// Note that in the case of a foreign unwinding operation, the exception object data may not be
|
||||
/// safely usable from Rust, and should not be directly exposed via the standard library. To
|
||||
/// prevent unsafe access, the library implementation may either abort the process or present an
|
||||
/// opaque error type to the user.
|
||||
///
|
||||
/// For more information, see the compiler's source, as well as the documentation for the stable
|
||||
/// version of this intrinsic, `std::panic::catch_unwind`.
|
||||
#[rustc_nounwind]
|
||||
pub fn catch_unwind(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32;
|
||||
/// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the
|
||||
/// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs.
|
||||
///
|
||||
/// `catch_fn` must not unwind.
|
||||
///
|
||||
/// The third argument is a function called if an unwind occurs (both Rust `panic` and foreign
|
||||
/// unwinds). This function takes the data pointer and a pointer to the target- and
|
||||
/// runtime-specific exception object that was caught.
|
||||
///
|
||||
/// Note that in the case of a foreign unwinding operation, the exception object data may not be
|
||||
/// safely usable from Rust, and should not be directly exposed via the standard library. To
|
||||
/// prevent unsafe access, the library implementation may either abort the process or present an
|
||||
/// opaque error type to the user.
|
||||
///
|
||||
/// For more information, see the compiler's source, as well as the documentation for the stable
|
||||
/// version of this intrinsic, `std::panic::catch_unwind`.
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_intrinsic_must_be_overridden]
|
||||
#[rustc_nounwind]
|
||||
pub unsafe fn catch_unwind(
|
||||
_try_fn: fn(*mut u8),
|
||||
_data: *mut u8,
|
||||
_catch_fn: fn(*mut u8, *mut u8),
|
||||
) -> i32 {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held
|
||||
/// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`.
|
||||
///
|
||||
/// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT`
|
||||
/// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered
|
||||
/// in ways that are not allowed for regular writes).
|
||||
#[rustc_nounwind]
|
||||
pub fn nontemporal_store<T>(ptr: *mut T, val: T);
|
||||
/// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held
|
||||
/// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`.
|
||||
///
|
||||
/// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT`
|
||||
/// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered
|
||||
/// in ways that are not allowed for regular writes).
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_intrinsic_must_be_overridden]
|
||||
#[rustc_nounwind]
|
||||
pub unsafe fn nontemporal_store<T>(_ptr: *mut T, _val: T) {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// See documentation of `<*const T>::offset_from` for details.
|
||||
|
|
|
|||
|
|
@ -597,6 +597,13 @@ impl Error for JoinPathsError {
|
|||
|
||||
/// Returns the path of the current user's home directory if known.
|
||||
///
|
||||
/// This may return `None` if getting the directory fails or if the platform does not have user home directories.
|
||||
///
|
||||
/// For storing user data and configuration it is often preferable to use more specific directories.
|
||||
/// For example, [XDG Base Directories] on Unix or the `LOCALAPPDATA` and `APPDATA` environment variables on Windows.
|
||||
///
|
||||
/// [XDG Base Directories]: https://specifications.freedesktop.org/basedir-spec/latest/
|
||||
///
|
||||
/// # Unix
|
||||
///
|
||||
/// - Returns the value of the 'HOME' environment variable if it is set
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::cell::{Cell, RefCell};
|
|||
use crate::error::Error;
|
||||
use crate::fmt;
|
||||
|
||||
/// A thread local storage key which owns its contents.
|
||||
/// A thread local storage (TLS) key which owns its contents.
|
||||
///
|
||||
/// This key uses the fastest possible implementation available to it for the
|
||||
/// target platform. It is instantiated with the [`thread_local!`] macro and the
|
||||
|
|
|
|||
|
|
@ -1,4 +1,25 @@
|
|||
type synthetic add -l lldb_lookup.synthetic_lookup -x ".*" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)String$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?str$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?\\[.+\\]$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::ffi::([a-z_]+::)+)OsString$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)VecDeque<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)BTreeSet<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)BTreeMap<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::collections::([a-z_]+::)+)HashMap<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::collections::([a-z_]+::)+)HashSet<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Rc<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Arc<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)Cell<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)Ref<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^core::num::([a-z_]+::)*NonZero.+$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::([a-z_]+::)+)PathBuf$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust
|
||||
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(.*)$" --category Rust
|
||||
type summary add -F _ -e -x -h "^.*$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)String$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?str$" --category Rust
|
||||
type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?\\[.+\\]$" --category Rust
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ use std::{io, mem};
|
|||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_errors::TerminalUrl;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::emitter::{
|
||||
DynEmitter, HumanEmitter, HumanReadableErrorType, OutputTheme, stderr_destination,
|
||||
};
|
||||
use rustc_errors::json::JsonEmitter;
|
||||
use rustc_errors::{ErrorGuaranteed, TerminalUrl};
|
||||
use rustc_feature::UnstableFeatures;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
|
||||
|
|
@ -326,7 +326,7 @@ pub(crate) fn run_global_ctxt(
|
|||
show_coverage: bool,
|
||||
render_options: RenderOptions,
|
||||
output_format: OutputFormat,
|
||||
) -> Result<(clean::Crate, RenderOptions, Cache), ErrorGuaranteed> {
|
||||
) -> (clean::Crate, RenderOptions, Cache) {
|
||||
// Certain queries assume that some checks were run elsewhere
|
||||
// (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425),
|
||||
// so type-check everything other than function bodies in this crate before running lints.
|
||||
|
|
@ -340,9 +340,7 @@ pub(crate) fn run_global_ctxt(
|
|||
tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
|
||||
});
|
||||
|
||||
if let Some(guar) = tcx.dcx().has_errors() {
|
||||
return Err(guar);
|
||||
}
|
||||
tcx.dcx().abort_if_errors();
|
||||
|
||||
tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx));
|
||||
tcx.sess.time("check_mod_attrs", || {
|
||||
|
|
@ -446,11 +444,9 @@ pub(crate) fn run_global_ctxt(
|
|||
LinkCollector { cx: &mut ctxt, visited_links: visited, ambiguous_links: ambiguous };
|
||||
collector.resolve_ambiguities();
|
||||
|
||||
if let Some(guar) = tcx.dcx().has_errors() {
|
||||
return Err(guar);
|
||||
}
|
||||
tcx.dcx().abort_if_errors();
|
||||
|
||||
Ok((krate, ctxt.render_options, ctxt.cache))
|
||||
(krate, ctxt.render_options, ctxt.cache)
|
||||
}
|
||||
|
||||
/// Due to <https://github.com/rust-lang/rust/pull/73566>,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ pub(crate) use markdown::test as test_markdown;
|
|||
use rustc_ast as ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::emitter::HumanReadableErrorType;
|
||||
use rustc_errors::{ColorConfig, DiagCtxtHandle, ErrorGuaranteed, FatalError};
|
||||
use rustc_errors::{ColorConfig, DiagCtxtHandle};
|
||||
use rustc_hir::CRATE_HIR_ID;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_interface::interface;
|
||||
|
|
@ -89,11 +89,7 @@ fn get_doctest_dir() -> io::Result<TempDir> {
|
|||
TempFileBuilder::new().prefix("rustdoctest").tempdir()
|
||||
}
|
||||
|
||||
pub(crate) fn run(
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
input: Input,
|
||||
options: RustdocOptions,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions) {
|
||||
let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name;
|
||||
|
||||
// See core::create_config for what's going on here.
|
||||
|
|
@ -167,7 +163,7 @@ pub(crate) fn run(
|
|||
Err(error) => return crate::wrap_return(dcx, Err(error)),
|
||||
};
|
||||
let args_path = temp_dir.path().join("rustdoc-cfgs");
|
||||
crate::wrap_return(dcx, generate_args_file(&args_path, &options))?;
|
||||
crate::wrap_return(dcx, generate_args_file(&args_path, &options));
|
||||
|
||||
let CreateRunnableDocTests {
|
||||
standalone_tests,
|
||||
|
|
@ -179,7 +175,7 @@ pub(crate) fn run(
|
|||
..
|
||||
} = interface::run_compiler(config, |compiler| {
|
||||
compiler.enter(|queries| {
|
||||
let collector = queries.global_ctxt()?.enter(|tcx| {
|
||||
let collector = queries.global_ctxt().enter(|tcx| {
|
||||
let crate_name = tcx.crate_name(LOCAL_CRATE).to_string();
|
||||
let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID);
|
||||
let opts = scrape_test_config(crate_name, crate_attrs, args_path);
|
||||
|
|
@ -196,13 +192,11 @@ pub(crate) fn run(
|
|||
|
||||
collector
|
||||
});
|
||||
if compiler.sess.dcx().has_errors().is_some() {
|
||||
FatalError.raise();
|
||||
}
|
||||
compiler.sess.dcx().abort_if_errors();
|
||||
|
||||
Ok(collector)
|
||||
collector
|
||||
})
|
||||
})?;
|
||||
});
|
||||
|
||||
run_tests(opts, &rustdoc_options, &unused_extern_reports, standalone_tests, mergeable_tests);
|
||||
|
||||
|
|
@ -246,8 +240,6 @@ pub(crate) fn run(
|
|||
eprintln!("{unused_extern_json}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn run_tests(
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ use std::process;
|
|||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError};
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_interface::interface;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::{ErrorOutputType, RustcOptGroup, make_crate_type_option};
|
||||
|
|
@ -179,7 +179,8 @@ pub fn main() {
|
|||
|
||||
let exit_code = rustc_driver::catch_with_exit_code(|| {
|
||||
let at_args = rustc_driver::args::raw_args(&early_dcx)?;
|
||||
main_args(&mut early_dcx, &at_args, using_internal_features)
|
||||
main_args(&mut early_dcx, &at_args, using_internal_features);
|
||||
Ok(())
|
||||
});
|
||||
process::exit(exit_code);
|
||||
}
|
||||
|
|
@ -699,13 +700,10 @@ fn usage(argv0: &str) {
|
|||
);
|
||||
}
|
||||
|
||||
/// A result type used by several functions under `main()`.
|
||||
type MainResult = Result<(), ErrorGuaranteed>;
|
||||
|
||||
pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) -> MainResult {
|
||||
pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) {
|
||||
match res {
|
||||
Ok(()) => dcx.has_errors().map_or(Ok(()), Err),
|
||||
Err(err) => Err(dcx.err(err)),
|
||||
Ok(()) => dcx.abort_if_errors(),
|
||||
Err(err) => dcx.fatal(err),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -714,17 +712,17 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
|
|||
renderopts: config::RenderOptions,
|
||||
cache: formats::cache::Cache,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> MainResult {
|
||||
) {
|
||||
match formats::run_format::<T>(krate, renderopts, cache, tcx) {
|
||||
Ok(_) => tcx.dcx().has_errors().map_or(Ok(()), Err),
|
||||
Ok(_) => tcx.dcx().abort_if_errors(),
|
||||
Err(e) => {
|
||||
let mut msg =
|
||||
tcx.dcx().struct_err(format!("couldn't generate documentation: {}", e.error));
|
||||
tcx.dcx().struct_fatal(format!("couldn't generate documentation: {}", e.error));
|
||||
let file = e.file.display().to_string();
|
||||
if !file.is_empty() {
|
||||
msg.note(format!("failed to create or modify \"{file}\""));
|
||||
}
|
||||
Err(msg.emit())
|
||||
msg.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -759,7 +757,7 @@ fn main_args(
|
|||
early_dcx: &mut EarlyDiagCtxt,
|
||||
at_args: &[String],
|
||||
using_internal_features: Arc<AtomicBool>,
|
||||
) -> MainResult {
|
||||
) {
|
||||
// Throw away the first argument, the name of the binary.
|
||||
// In case of at_args being empty, as might be the case by
|
||||
// passing empty argument array to execve under some platforms,
|
||||
|
|
@ -770,7 +768,7 @@ fn main_args(
|
|||
// the compiler with @empty_file as argv[0] and no more arguments.
|
||||
let at_args = at_args.get(1..).unwrap_or_default();
|
||||
|
||||
let args = rustc_driver::args::arg_expand_all(early_dcx, at_args)?;
|
||||
let args = rustc_driver::args::arg_expand_all(early_dcx, at_args);
|
||||
|
||||
let mut options = getopts::Options::new();
|
||||
for option in opts() {
|
||||
|
|
@ -788,7 +786,7 @@ fn main_args(
|
|||
let (input, options, render_options) =
|
||||
match config::Options::from_matches(early_dcx, &matches, args) {
|
||||
Some(opts) => opts,
|
||||
None => return Ok(()),
|
||||
None => return,
|
||||
};
|
||||
|
||||
let dcx =
|
||||
|
|
@ -853,11 +851,11 @@ fn main_args(
|
|||
|
||||
if sess.opts.describe_lints {
|
||||
rustc_driver::describe_lints(sess);
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
|
||||
compiler.enter(|queries| {
|
||||
let Ok(mut gcx) = queries.global_ctxt() else { FatalError.raise() };
|
||||
let mut gcx = queries.global_ctxt();
|
||||
if sess.dcx().has_errors().is_some() {
|
||||
sess.dcx().fatal("Compilation failed, aborting rustdoc");
|
||||
}
|
||||
|
|
@ -865,7 +863,7 @@ fn main_args(
|
|||
gcx.enter(|tcx| {
|
||||
let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || {
|
||||
core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
|
||||
})?;
|
||||
});
|
||||
info!("finished with rustc");
|
||||
|
||||
if let Some(options) = scrape_examples_options {
|
||||
|
|
@ -884,10 +882,10 @@ fn main_args(
|
|||
if show_coverage {
|
||||
// if we ran coverage, bail early, we don't need to also generate docs at this point
|
||||
// (also we didn't load in any of the useful passes)
|
||||
return Ok(());
|
||||
return;
|
||||
} else if run_check {
|
||||
// Since we're in "check" mode, no need to generate anything beyond this point.
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
|
||||
info!("going to format");
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use rustc_data_structures::sync::{Lock, Lrc};
|
||||
use rustc_errors::emitter::Emitter;
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::translation::{Translate, to_fluent_args};
|
||||
use rustc_errors::{Applicability, DiagCtxt, DiagInner, LazyFallbackBundle};
|
||||
use rustc_parse::{source_str_to_stream, unwrap_or_emit_fatal};
|
||||
|
|
@ -155,7 +156,7 @@ impl Translate for BufferEmitter {
|
|||
}
|
||||
|
||||
impl Emitter for BufferEmitter {
|
||||
fn emit_diagnostic(&mut self, diag: DiagInner) {
|
||||
fn emit_diagnostic(&mut self, diag: DiagInner, _registry: &Registry) {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
|
||||
let fluent_args = to_fluent_args(diag.args.iter());
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use rustc_data_structures::fx::FxIndexMap;
|
|||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{self as hir};
|
||||
use rustc_interface::interface;
|
||||
use rustc_macros::{Decodable, Encodable};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
|
|
@ -275,7 +274,7 @@ pub(crate) fn run(
|
|||
tcx: TyCtxt<'_>,
|
||||
options: ScrapeExamplesOptions,
|
||||
bin_crate: bool,
|
||||
) -> interface::Result<()> {
|
||||
) {
|
||||
let inner = move || -> Result<(), String> {
|
||||
// Generates source files for examples
|
||||
renderopts.no_emit_shared = true;
|
||||
|
|
@ -329,8 +328,6 @@ pub(crate) fn run(
|
|||
if let Err(e) = inner() {
|
||||
tcx.dcx().fatal(e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Note: the DiagCtxt must be passed in explicitly because sess isn't available while parsing
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
|
|||
// In the case of only two patterns, replacement adds net characters.
|
||||
| Ref(_, Mutability::Not)
|
||||
// Dealt with elsewhere.
|
||||
| Or(_) | Paren(_) | Deref(_) => false,
|
||||
| Or(_) | Paren(_) | Deref(_) | Guard(..) => false,
|
||||
// Transform `box x | ... | box y` into `box (x | y)`.
|
||||
//
|
||||
// The cases below until `Slice(...)` deal with *singleton* products.
|
||||
|
|
|
|||
|
|
@ -236,7 +236,8 @@ pub fn main() {
|
|||
let mut args: Vec<String> = orig_args.clone();
|
||||
pass_sysroot_env_if_given(&mut args, sys_root_env);
|
||||
|
||||
return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
|
||||
rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if orig_args.iter().any(|a| a == "--version" || a == "-V") {
|
||||
|
|
@ -296,12 +297,13 @@ pub fn main() {
|
|||
args.extend(clippy_args);
|
||||
rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var })
|
||||
.set_using_internal_features(using_internal_features)
|
||||
.run()
|
||||
.run();
|
||||
} else {
|
||||
rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var })
|
||||
.set_using_internal_features(using_internal_features)
|
||||
.run()
|
||||
.run();
|
||||
}
|
||||
return Ok(());
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ use walkdir::WalkDir;
|
|||
|
||||
use self::header::{EarlyProps, make_test_description};
|
||||
use crate::common::{
|
||||
Config, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path, output_base_dir,
|
||||
output_relative_path,
|
||||
CompareMode, Config, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path,
|
||||
output_base_dir, output_relative_path,
|
||||
};
|
||||
use crate::header::HeadersCache;
|
||||
use crate::util::logv;
|
||||
|
|
@ -273,6 +273,15 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
} else {
|
||||
matches.free.clone()
|
||||
};
|
||||
let compare_mode = matches.opt_str("compare-mode").map(|s| {
|
||||
s.parse().unwrap_or_else(|_| {
|
||||
let variants: Vec<_> = CompareMode::STR_VARIANTS.iter().copied().collect();
|
||||
panic!(
|
||||
"`{s}` is not a valid value for `--compare-mode`, it should be one of: {}",
|
||||
variants.join(", ")
|
||||
);
|
||||
})
|
||||
});
|
||||
Config {
|
||||
bless: matches.opt_present("bless"),
|
||||
compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
|
||||
|
|
@ -342,9 +351,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
only_modified: matches.opt_present("only-modified"),
|
||||
color,
|
||||
remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
|
||||
compare_mode: matches
|
||||
.opt_str("compare-mode")
|
||||
.map(|s| s.parse().expect("invalid --compare-mode provided")),
|
||||
compare_mode,
|
||||
rustfix_coverage: matches.opt_present("rustfix-coverage"),
|
||||
has_html_tidy,
|
||||
has_enzyme,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use crate::common::{
|
|||
UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, Ui, expected_output_path, incremental_dir,
|
||||
output_base_dir, output_base_name, output_testname_unique,
|
||||
};
|
||||
use crate::compute_diff::{write_diff, write_filtered_diff};
|
||||
use crate::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff};
|
||||
use crate::errors::{self, Error, ErrorKind};
|
||||
use crate::header::TestProps;
|
||||
use crate::read2::{Truncated, read2_abbreviated};
|
||||
|
|
@ -2295,17 +2295,31 @@ impl<'test> TestCx<'test> {
|
|||
match output_kind {
|
||||
TestOutput::Compile => {
|
||||
if !self.props.dont_check_compiler_stdout {
|
||||
errors +=
|
||||
self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout);
|
||||
errors += self.compare_output(
|
||||
stdout_kind,
|
||||
&normalized_stdout,
|
||||
&proc_res.stdout,
|
||||
&expected_stdout,
|
||||
);
|
||||
}
|
||||
if !self.props.dont_check_compiler_stderr {
|
||||
errors +=
|
||||
self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr);
|
||||
errors += self.compare_output(
|
||||
stderr_kind,
|
||||
&normalized_stderr,
|
||||
&stderr,
|
||||
&expected_stderr,
|
||||
);
|
||||
}
|
||||
}
|
||||
TestOutput::Run => {
|
||||
errors += self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout);
|
||||
errors += self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr);
|
||||
errors += self.compare_output(
|
||||
stdout_kind,
|
||||
&normalized_stdout,
|
||||
&proc_res.stdout,
|
||||
&expected_stdout,
|
||||
);
|
||||
errors +=
|
||||
self.compare_output(stderr_kind, &normalized_stderr, &stderr, &expected_stderr);
|
||||
}
|
||||
}
|
||||
errors
|
||||
|
|
@ -2533,7 +2547,13 @@ impl<'test> TestCx<'test> {
|
|||
}
|
||||
}
|
||||
|
||||
fn compare_output(&self, stream: &str, actual: &str, expected: &str) -> usize {
|
||||
fn compare_output(
|
||||
&self,
|
||||
stream: &str,
|
||||
actual: &str,
|
||||
actual_unnormalized: &str,
|
||||
expected: &str,
|
||||
) -> usize {
|
||||
let are_different = match (self.force_color_svg(), expected.find('\n'), actual.find('\n')) {
|
||||
// FIXME: We ignore the first line of SVG files
|
||||
// because the width parameter is non-deterministic.
|
||||
|
|
@ -2590,28 +2610,14 @@ impl<'test> TestCx<'test> {
|
|||
if expected.is_empty() {
|
||||
println!("normalized {}:\n{}\n", stream, actual);
|
||||
} else {
|
||||
println!("diff of {stream}:\n");
|
||||
if let Some(diff_command) = self.config.diff_command.as_deref() {
|
||||
let mut args = diff_command.split_whitespace();
|
||||
let name = args.next().unwrap();
|
||||
match Command::new(name)
|
||||
.args(args)
|
||||
.args([&expected_path, &actual_path])
|
||||
.output()
|
||||
{
|
||||
Err(err) => {
|
||||
self.fatal(&format!(
|
||||
"failed to call custom diff command `{diff_command}`: {err}"
|
||||
));
|
||||
}
|
||||
Ok(output) => {
|
||||
let output = String::from_utf8_lossy(&output.stdout);
|
||||
print!("{output}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print!("{}", write_diff(expected, actual, 3));
|
||||
}
|
||||
self.show_diff(
|
||||
stream,
|
||||
&expected_path,
|
||||
&actual_path,
|
||||
expected,
|
||||
actual,
|
||||
actual_unnormalized,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Delete non-revision .stderr/.stdout file if revisions are used.
|
||||
|
|
@ -2633,6 +2639,76 @@ impl<'test> TestCx<'test> {
|
|||
if self.config.bless { 0 } else { 1 }
|
||||
}
|
||||
|
||||
/// Returns whether to show the full stderr/stdout.
|
||||
fn show_diff(
|
||||
&self,
|
||||
stream: &str,
|
||||
expected_path: &Path,
|
||||
actual_path: &Path,
|
||||
expected: &str,
|
||||
actual: &str,
|
||||
actual_unnormalized: &str,
|
||||
) {
|
||||
eprintln!("diff of {stream}:\n");
|
||||
if let Some(diff_command) = self.config.diff_command.as_deref() {
|
||||
let mut args = diff_command.split_whitespace();
|
||||
let name = args.next().unwrap();
|
||||
match Command::new(name).args(args).args([expected_path, actual_path]).output() {
|
||||
Err(err) => {
|
||||
self.fatal(&format!(
|
||||
"failed to call custom diff command `{diff_command}`: {err}"
|
||||
));
|
||||
}
|
||||
Ok(output) => {
|
||||
let output = String::from_utf8_lossy(&output.stdout);
|
||||
eprint!("{output}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprint!("{}", write_diff(expected, actual, 3));
|
||||
}
|
||||
|
||||
// NOTE: argument order is important, we need `actual` to be on the left so the line number match up when we compare it to `actual_unnormalized` below.
|
||||
let diff_results = make_diff(actual, expected, 0);
|
||||
|
||||
let (mut mismatches_normalized, mut mismatch_line_nos) = (String::new(), vec![]);
|
||||
for hunk in diff_results {
|
||||
let mut line_no = hunk.line_number;
|
||||
for line in hunk.lines {
|
||||
// NOTE: `Expected` is actually correct here, the argument order is reversed so our line numbers match up
|
||||
if let DiffLine::Expected(normalized) = line {
|
||||
mismatches_normalized += &normalized;
|
||||
mismatches_normalized += "\n";
|
||||
mismatch_line_nos.push(line_no);
|
||||
line_no += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut mismatches_unnormalized = String::new();
|
||||
let diff_normalized = make_diff(actual, actual_unnormalized, 0);
|
||||
for hunk in diff_normalized {
|
||||
if mismatch_line_nos.contains(&hunk.line_number) {
|
||||
for line in hunk.lines {
|
||||
if let DiffLine::Resulting(unnormalized) = line {
|
||||
mismatches_unnormalized += &unnormalized;
|
||||
mismatches_unnormalized += "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let normalized_diff = make_diff(&mismatches_normalized, &mismatches_unnormalized, 0);
|
||||
// HACK: instead of checking if each hunk is empty, this only checks if the whole input is empty. we should be smarter about this so we don't treat added or removed output as normalized.
|
||||
if !normalized_diff.is_empty()
|
||||
&& !mismatches_unnormalized.is_empty()
|
||||
&& !mismatches_normalized.is_empty()
|
||||
{
|
||||
eprintln!("Note: some mismatched output was normalized before being compared");
|
||||
// FIXME: respect diff_command
|
||||
eprint!("{}", write_diff(&mismatches_unnormalized, &mismatches_normalized, 0));
|
||||
}
|
||||
}
|
||||
|
||||
fn check_and_prune_duplicate_outputs(
|
||||
&self,
|
||||
proc_res: &ProcRes,
|
||||
|
|
|
|||
|
|
@ -39,8 +39,12 @@ impl<'test> TestCx<'test> {
|
|||
let expected_coverage_dump = self.load_expected_output(kind);
|
||||
let actual_coverage_dump = self.normalize_output(&proc_res.stdout, &[]);
|
||||
|
||||
let coverage_dump_errors =
|
||||
self.compare_output(kind, &actual_coverage_dump, &expected_coverage_dump);
|
||||
let coverage_dump_errors = self.compare_output(
|
||||
kind,
|
||||
&actual_coverage_dump,
|
||||
&proc_res.stdout,
|
||||
&expected_coverage_dump,
|
||||
);
|
||||
|
||||
if coverage_dump_errors > 0 {
|
||||
self.fatal_proc_rec(
|
||||
|
|
@ -135,8 +139,12 @@ impl<'test> TestCx<'test> {
|
|||
self.fatal_proc_rec(&err, &proc_res);
|
||||
});
|
||||
|
||||
let coverage_errors =
|
||||
self.compare_output(kind, &normalized_actual_coverage, &expected_coverage);
|
||||
let coverage_errors = self.compare_output(
|
||||
kind,
|
||||
&normalized_actual_coverage,
|
||||
&proc_res.stdout,
|
||||
&expected_coverage,
|
||||
);
|
||||
|
||||
if coverage_errors > 0 {
|
||||
self.fatal_proc_rec(
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ impl TestCx<'_> {
|
|||
)
|
||||
});
|
||||
|
||||
errors += self.compare_output("fixed", &fixed_code, &expected_fixed);
|
||||
errors += self.compare_output("fixed", &fixed_code, &fixed_code, &expected_fixed);
|
||||
} else if !expected_fixed.is_empty() {
|
||||
panic!(
|
||||
"the `//@ run-rustfix` directive wasn't found but a `*.fixed` \
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
728f2daab42ba8f1b3d5caab62495798d1eabfa1
|
||||
1b3fb316751227d30b1523ed0e3f00d83956d4d0
|
||||
|
|
|
|||
|
|
@ -290,7 +290,8 @@ fn run_compiler(
|
|||
let exit_code = rustc_driver::catch_with_exit_code(move || {
|
||||
rustc_driver::RunCompiler::new(&args, callbacks)
|
||||
.set_using_internal_features(using_internal_features)
|
||||
.run()
|
||||
.run();
|
||||
Ok(())
|
||||
});
|
||||
std::process::exit(exit_code)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||
|
||||
use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
|
||||
use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination};
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::translation::Translate;
|
||||
use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
|
||||
use rustc_session::parse::ParseSess as RawParseSess;
|
||||
|
|
@ -38,10 +39,10 @@ struct SilentOnIgnoredFilesEmitter {
|
|||
}
|
||||
|
||||
impl SilentOnIgnoredFilesEmitter {
|
||||
fn handle_non_ignoreable_error(&mut self, diag: DiagInner) {
|
||||
fn handle_non_ignoreable_error(&mut self, diag: DiagInner, registry: &Registry) {
|
||||
self.has_non_ignorable_parser_errors = true;
|
||||
self.can_reset.store(false, Ordering::Release);
|
||||
self.emitter.emit_diagnostic(diag);
|
||||
self.emitter.emit_diagnostic(diag, registry);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,9 +61,9 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
|
|||
None
|
||||
}
|
||||
|
||||
fn emit_diagnostic(&mut self, diag: DiagInner) {
|
||||
fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry) {
|
||||
if diag.level() == DiagnosticLevel::Fatal {
|
||||
return self.handle_non_ignoreable_error(diag);
|
||||
return self.handle_non_ignoreable_error(diag, registry);
|
||||
}
|
||||
if let Some(primary_span) = &diag.span.primary_span() {
|
||||
let file_name = self.source_map.span_to_filename(*primary_span);
|
||||
|
|
@ -80,7 +81,7 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
|
|||
}
|
||||
};
|
||||
}
|
||||
self.handle_non_ignoreable_error(diag);
|
||||
self.handle_non_ignoreable_error(diag, registry);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -358,7 +359,7 @@ mod tests {
|
|||
None
|
||||
}
|
||||
|
||||
fn emit_diagnostic(&mut self, _diag: DiagInner) {
|
||||
fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {
|
||||
self.num_emitted_errors.fetch_add(1, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
|
@ -412,6 +413,7 @@ mod tests {
|
|||
SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
|
||||
source,
|
||||
);
|
||||
let registry = Registry::new(&[]);
|
||||
let mut emitter = build_emitter(
|
||||
Lrc::clone(&num_emitted_errors),
|
||||
Lrc::clone(&can_reset_errors),
|
||||
|
|
@ -420,7 +422,7 @@ mod tests {
|
|||
);
|
||||
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
|
||||
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
|
||||
emitter.emit_diagnostic(fatal_diagnostic);
|
||||
emitter.emit_diagnostic(fatal_diagnostic, ®istry);
|
||||
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
|
||||
assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
|
||||
}
|
||||
|
|
@ -437,6 +439,7 @@ mod tests {
|
|||
SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
|
||||
source,
|
||||
);
|
||||
let registry = Registry::new(&[]);
|
||||
let mut emitter = build_emitter(
|
||||
Lrc::clone(&num_emitted_errors),
|
||||
Lrc::clone(&can_reset_errors),
|
||||
|
|
@ -445,7 +448,7 @@ mod tests {
|
|||
);
|
||||
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
|
||||
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
|
||||
emitter.emit_diagnostic(non_fatal_diagnostic);
|
||||
emitter.emit_diagnostic(non_fatal_diagnostic, ®istry);
|
||||
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
|
||||
assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
|
||||
}
|
||||
|
|
@ -461,6 +464,7 @@ mod tests {
|
|||
SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
|
||||
source,
|
||||
);
|
||||
let registry = Registry::new(&[]);
|
||||
let mut emitter = build_emitter(
|
||||
Lrc::clone(&num_emitted_errors),
|
||||
Lrc::clone(&can_reset_errors),
|
||||
|
|
@ -469,7 +473,7 @@ mod tests {
|
|||
);
|
||||
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
|
||||
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
|
||||
emitter.emit_diagnostic(non_fatal_diagnostic);
|
||||
emitter.emit_diagnostic(non_fatal_diagnostic, ®istry);
|
||||
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
|
||||
assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
|
||||
}
|
||||
|
|
@ -497,6 +501,7 @@ mod tests {
|
|||
SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
|
||||
fatal_source,
|
||||
);
|
||||
let registry = Registry::new(&[]);
|
||||
let mut emitter = build_emitter(
|
||||
Lrc::clone(&num_emitted_errors),
|
||||
Lrc::clone(&can_reset_errors),
|
||||
|
|
@ -508,9 +513,9 @@ mod tests {
|
|||
let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
|
||||
let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
|
||||
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
|
||||
emitter.emit_diagnostic(bar_diagnostic);
|
||||
emitter.emit_diagnostic(foo_diagnostic);
|
||||
emitter.emit_diagnostic(fatal_diagnostic);
|
||||
emitter.emit_diagnostic(bar_diagnostic, ®istry);
|
||||
emitter.emit_diagnostic(foo_diagnostic, ®istry);
|
||||
emitter.emit_diagnostic(fatal_diagnostic, ®istry);
|
||||
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
|
||||
assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
|
|||
| ast::PatKind::MacCall(..)
|
||||
| ast::PatKind::Slice(..)
|
||||
| ast::PatKind::Path(..)
|
||||
| ast::PatKind::Range(..) => false,
|
||||
| ast::PatKind::Range(..)
|
||||
| ast::PatKind::Guard(..) => false,
|
||||
ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1,
|
||||
ast::PatKind::TupleStruct(_, ref path, ref subpats) => {
|
||||
path.segments.len() <= 1 && subpats.len() <= 1
|
||||
|
|
@ -338,8 +339,9 @@ impl Rewrite for Pat {
|
|||
.max_width_error(shape.width, self.span)?,
|
||||
)
|
||||
.map(|inner_pat| format!("({})", inner_pat)),
|
||||
PatKind::Err(_) => Err(RewriteError::Unknown),
|
||||
PatKind::Guard(..) => Ok(context.snippet(self.span).to_string()),
|
||||
PatKind::Deref(_) => Err(RewriteError::Unknown),
|
||||
PatKind::Err(_) => Err(RewriteError::Unknown),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
src/tools/rustfmt/tests/target/guard_patterns.rs
Normal file
12
src/tools/rustfmt/tests/target/guard_patterns.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#![feature(guard_patterns)]
|
||||
|
||||
fn main() {
|
||||
match user.subscription_plan() {
|
||||
(Plan::Regular if user.credit() >= 100) | (Plan::Premium if user.credit() >= 80) => {
|
||||
// Complete the transaction.
|
||||
}
|
||||
_ => {
|
||||
// The user doesn't have enough credit, return an error message.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//@ known-bug: #121363
|
||||
//@ compile-flags: -Zmir-opt-level=5 --crate-type lib
|
||||
//@ compile-flags: -Zmir-enable-passes=+GVN --crate-type lib
|
||||
|
||||
#![feature(trivial_bounds)]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//@ known-bug: rust-lang/rust#128094
|
||||
//@ compile-flags: -Zmir-opt-level=5 --edition=2018
|
||||
//@ compile-flags: -Zmir-enable-passes=+GVN --edition=2018
|
||||
|
||||
pub enum Request {
|
||||
TestSome(T),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//@ known-bug: rust-lang/rust#129095
|
||||
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir
|
||||
//@ compile-flags: -Zmir-enable-passes=+GVN -Zmir-enable-passes=+Inline -Zvalidate-mir
|
||||
|
||||
pub fn function_with_bytes<const BYTES: &'static [u8; 4]>() -> &'static [u8] {
|
||||
BYTES
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//@ known-bug: rust-lang/rust#129109
|
||||
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir
|
||||
//@ compile-flags: -Zmir-enable-passes=+GVN -Zvalidate-mir
|
||||
|
||||
extern "C" {
|
||||
pub static mut symbol: [i8];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//@ known-bug: #130970
|
||||
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir
|
||||
//@ compile-flags: -Zmir-enable-passes=+GVN -Zvalidate-mir
|
||||
|
||||
fn main() {
|
||||
extern "C" {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//@ known-bug: #131347
|
||||
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir
|
||||
//@ compile-flags: -Zmir-enable-passes=+GVN -Zmir-enable-passes=+Inline -Zvalidate-mir
|
||||
|
||||
struct S;
|
||||
static STUFF: [i8] = [0; S::N];
|
||||
|
|
|
|||
9
tests/crashes/131451.rs
Normal file
9
tests/crashes/131451.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
//@ known-bug: #131451
|
||||
//@ needs-rustc-debug-assertions
|
||||
//@ compile-flags: -Zmir-enable-passes=+GVN -Zmir-enable-passes=+JumpThreading --crate-type=lib
|
||||
|
||||
pub fn fun(terminate: bool) {
|
||||
while true {}
|
||||
|
||||
while !terminate {}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//@ known-bug: #131507
|
||||
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir
|
||||
//@ compile-flags: -Zmir-enable-passes=+GVN -Zmir-enable-passes=+Inline -Zvalidate-mir
|
||||
#![feature(non_lifetime_binders)]
|
||||
|
||||
fn brick()
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@
|
|||
//@ [cfail2] compile-flags: --test --extern aux={{build-base}}/circular-dependencies/auxiliary/libcircular_dependencies_aux.rmeta -L dependency={{build-base}}/circular-dependencies
|
||||
|
||||
pub struct Foo;
|
||||
//[cfail2]~^ NOTE `Foo` is defined in the current crate
|
||||
//[cfail2]~| NOTE `Foo` is defined in the current crate
|
||||
//[cfail2]~| NOTE `circular_dependencies::Foo` is defined in crate `circular_dependencies`
|
||||
//[cfail2]~| NOTE `circular_dependencies::Foo` is defined in crate `circular_dependencies`
|
||||
//[cfail2]~^ NOTE the crate `circular_dependencies` is compiled multiple times, possibly with different configurations
|
||||
//[cfail2]~| NOTE the crate `circular_dependencies` is compiled multiple times, possibly with different configurations
|
||||
//[cfail2]~| NOTE this is the expected type `Foo`
|
||||
//[cfail2]~| NOTE this is the expected type `circular_dependencies::Foo`
|
||||
//[cfail2]~| NOTE this is the found type `Foo`
|
||||
//[cfail2]~| NOTE this is the found type `circular_dependencies::Foo`
|
||||
|
||||
pub fn consume_foo(_: Foo) {}
|
||||
//[cfail2]~^ NOTE function defined here
|
||||
|
|
@ -24,14 +26,12 @@ fn test() {
|
|||
//[cfail2]~^ ERROR mismatched types [E0308]
|
||||
//[cfail2]~| NOTE expected `circular_dependencies::Foo`, found `Foo`
|
||||
//[cfail2]~| NOTE arguments to this function are incorrect
|
||||
//[cfail2]~| NOTE `Foo` and `circular_dependencies::Foo` have similar names, but are actually distinct types
|
||||
//[cfail2]~| NOTE the crate `circular_dependencies` is compiled multiple times, possibly with different configurations
|
||||
//[cfail2]~| NOTE function defined here
|
||||
//[cfail2]~| NOTE one version of crate `circular_dependencies` used here, as a dependency of crate `circular_dependencies_aux`
|
||||
//[cfail2]~| NOTE one version of crate `circular_dependencies` used here, as a dependency of crate `circular_dependencies_aux`
|
||||
|
||||
consume_foo(aux::produce_foo());
|
||||
//[cfail2]~^ ERROR mismatched types [E0308]
|
||||
//[cfail2]~| NOTE expected `Foo`, found `circular_dependencies::Foo`
|
||||
//[cfail2]~| NOTE arguments to this function are incorrect
|
||||
//[cfail2]~| NOTE `circular_dependencies::Foo` and `Foo` have similar names, but are actually distinct types
|
||||
//[cfail2]~| NOTE the crate `circular_dependencies` is compiled multiple times, possibly with different configurations
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ note: there are multiple different versions of crate `foo` in the dependency gra
|
|||
--> foo-current.rs:7:1
|
||||
|
|
||||
4 | extern crate foo;
|
||||
| ----------------- one version of crate `foo` is used here, as a direct dependency of the current crate
|
||||
| ----------------- one version of crate `foo` used here, as a direct dependency of the current crate
|
||||
5 |
|
||||
6 | pub struct Struct;
|
||||
| ----------------- this type implements the required trait
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@ pub trait Trait {
|
|||
fn foo(&self);
|
||||
fn bar();
|
||||
}
|
||||
pub trait Trait2 {}
|
||||
impl Trait for Type {
|
||||
fn foo(&self) {}
|
||||
fn bar() {}
|
||||
}
|
||||
pub fn do_something<X: Trait>(_: X) {}
|
||||
pub fn do_something_type(_: Type) {}
|
||||
pub fn do_something_trait(_: Box<dyn Trait2>) {}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,12 @@ pub trait Trait {
|
|||
fn foo(&self);
|
||||
fn bar();
|
||||
}
|
||||
pub trait Trait2 {}
|
||||
impl Trait2 for Type {}
|
||||
impl Trait for Type {
|
||||
fn foo(&self) {}
|
||||
fn bar() {}
|
||||
}
|
||||
pub fn do_something<X: Trait>(_: X) {}
|
||||
pub fn do_something_type(_: Type) {}
|
||||
pub fn do_something_trait(_: Box<dyn Trait2>) {}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#![crate_type = "rlib"]
|
||||
|
||||
extern crate dependency;
|
||||
pub use dependency::Type;
|
||||
pub use dependency::{Trait2, Type, do_something_trait, do_something_type};
|
||||
pub struct OtherType;
|
||||
impl dependency::Trait for OtherType {
|
||||
fn foo(&self) {}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
extern crate dep_2_reexport;
|
||||
extern crate dependency;
|
||||
use dep_2_reexport::{OtherType, Type};
|
||||
use dependency::{Trait, do_something};
|
||||
use dep_2_reexport::{OtherType, Trait2, Type};
|
||||
use dependency::{Trait, do_something, do_something_trait, do_something_type};
|
||||
|
||||
fn main() {
|
||||
do_something(Type);
|
||||
Type.foo();
|
||||
Type::bar();
|
||||
do_something(OtherType);
|
||||
do_something_type(Type);
|
||||
do_something_trait(Box::new(Type) as Box<dyn Trait2>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ LL | pub trait Trait {
|
|||
::: replaced
|
||||
|
|
||||
LL | extern crate dep_2_reexport;
|
||||
| ---------------------------- one version of crate `dependency` is used here, as a dependency of crate `foo`
|
||||
| ---------------------------- one version of crate `dependency` used here, as a dependency of crate `foo`
|
||||
LL | extern crate dependency;
|
||||
| ------------------------ one version of crate `dependency` is used here, as a direct dependency of the current crate
|
||||
| ------------------------ one version of crate `dependency` used here, as a direct dependency of the current crate
|
||||
|
|
||||
::: replaced
|
||||
|
|
||||
|
|
@ -51,7 +51,7 @@ LL | fn foo(&self);
|
|||
|
|
||||
::: replaced
|
||||
|
|
||||
LL | use dependency::{Trait, do_something};
|
||||
LL | use dependency::{Trait, do_something, do_something_trait, do_something_type};
|
||||
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`
|
||||
|
|
||||
::: replaced
|
||||
|
|
@ -76,7 +76,7 @@ LL | fn bar();
|
|||
|
|
||||
::: replaced
|
||||
|
|
||||
LL | use dependency::{Trait, do_something};
|
||||
LL | use dependency::{Trait, do_something, do_something_trait, do_something_type};
|
||||
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`
|
||||
|
|
||||
::: replaced
|
||||
|
|
@ -101,9 +101,9 @@ LL | pub trait Trait {
|
|||
::: replaced
|
||||
|
|
||||
LL | extern crate dep_2_reexport;
|
||||
| ---------------------------- one version of crate `dependency` is used here, as a dependency of crate `foo`
|
||||
| ---------------------------- one version of crate `dependency` used here, as a dependency of crate `foo`
|
||||
LL | extern crate dependency;
|
||||
| ------------------------ one version of crate `dependency` is used here, as a direct dependency of the current crate
|
||||
| ------------------------ one version of crate `dependency` used here, as a direct dependency of the current crate
|
||||
|
|
||||
::: replaced
|
||||
|
|
||||
|
|
@ -121,7 +121,71 @@ note: required by a bound in `do_something`
|
|||
LL | pub fn do_something<X: Trait>(_: X) {}
|
||||
| ^^^^^ required by this bound in `do_something`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error[E0308]: mismatched types
|
||||
--> replaced
|
||||
|
|
||||
LL | do_something_type(Type);
|
||||
| ----------------- ^^^^ expected `dependency::Type`, found `dep_2_reexport::Type`
|
||||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
note: two different versions of crate `dependency` are being used; two types coming from two different versions of the same crate are different types even if they look the same
|
||||
--> replaced
|
||||
|
|
||||
LL | pub struct Type(pub i32);
|
||||
| ^^^^^^^^^^^^^^^ this is the expected type `dependency::Type`
|
||||
|
|
||||
::: replaced
|
||||
|
|
||||
LL | pub struct Type;
|
||||
| ^^^^^^^^^^^^^^^ this is the found type `dep_2_reexport::Type`
|
||||
|
|
||||
::: replaced
|
||||
|
|
||||
LL | extern crate dep_2_reexport;
|
||||
| ---------------------------- one version of crate `dependency` used here, as a dependency of crate `foo`
|
||||
LL | extern crate dependency;
|
||||
| ------------------------ one version of crate `dependency` used here, as a direct dependency of the current crate
|
||||
= help: you can use `cargo tree` to explore your dependency tree
|
||||
note: function defined here
|
||||
--> replaced
|
||||
|
|
||||
LL | pub fn do_something_type(_: Type) {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some errors have detailed explanations: E0277, E0599.
|
||||
error[E0308]: mismatched types
|
||||
--> replaced
|
||||
|
|
||||
LL | do_something_trait(Box::new(Type) as Box<dyn Trait2>);
|
||||
| ------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `dependency::Trait2`, found trait `dep_2_reexport::Trait2`
|
||||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
note: two different versions of crate `dependency` are being used; two types coming from two different versions of the same crate are different types even if they look the same
|
||||
--> replaced
|
||||
|
|
||||
LL | pub trait Trait2 {}
|
||||
| ^^^^^^^^^^^^^^^^ this is the expected trait `dependency::Trait2`
|
||||
|
|
||||
::: replaced
|
||||
|
|
||||
LL | pub trait Trait2 {}
|
||||
| ^^^^^^^^^^^^^^^^ this is the found trait `dep_2_reexport::Trait2`
|
||||
|
|
||||
::: replaced
|
||||
|
|
||||
LL | extern crate dep_2_reexport;
|
||||
| ---------------------------- one version of crate `dependency` used here, as a dependency of crate `foo`
|
||||
LL | extern crate dependency;
|
||||
| ------------------------ one version of crate `dependency` used here, as a direct dependency of the current crate
|
||||
= help: you can use `cargo tree` to explore your dependency tree
|
||||
note: function defined here
|
||||
--> replaced
|
||||
|
|
||||
LL | pub fn do_something_trait(_: Box<dyn Trait2>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0277, E0308, E0599.
|
||||
For more information about an error, try `rustc --explain E0277`.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
pub fn baz<T>(t: std::ops::Range<T>) {
|
||||
for _ in t {}
|
||||
}
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
error[E0277]: the trait bound `T: Step` is not satisfied
|
||||
--> missing-bound.rs:2:14
|
||||
|
|
||||
2 | for _ in t {}
|
||||
| ^ the trait `Step` is not implemented for `T`
|
||||
|
|
||||
= note: required for `std::ops::Range<T>` to implement `Iterator`
|
||||
= note: required for `std::ops::Range<T>` to implement `IntoIterator`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
24
tests/run-make/missing-unstable-trait-bound/rmake.rs
Normal file
24
tests/run-make/missing-unstable-trait-bound/rmake.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
//@ only-linux
|
||||
//@ ignore-wasm32
|
||||
//@ ignore-wasm64
|
||||
// ignore-tidy-linelength
|
||||
|
||||
// Ensure that on stable we don't suggest restricting with an unsafe trait and we continue
|
||||
// mentioning the rest of the obligation chain.
|
||||
|
||||
use run_make_support::{diff, rust_lib_name, rustc};
|
||||
|
||||
fn main() {
|
||||
let out = rustc()
|
||||
.env("RUSTC_BOOTSTRAP", "-1")
|
||||
.input("missing-bound.rs")
|
||||
.run_fail()
|
||||
.assert_stderr_not_contains("help: consider restricting type parameter `T`")
|
||||
.assert_stderr_contains(
|
||||
r#"
|
||||
= note: required for `std::ops::Range<T>` to implement `Iterator`
|
||||
= note: required for `std::ops::Range<T>` to implement `IntoIterator`"#,
|
||||
)
|
||||
.stderr_utf8();
|
||||
diff().expected_file("missing-bound.stderr").actual_text("(stable rustc)", &out).run()
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ error[E0220]: associated type `Assoc` not found for `V`
|
|||
LL | pub type Foo<V> = impl Trait<V::Assoc>;
|
||||
| ^^^^^ there is an associated type `Assoc` in the trait `TraitWithAssoc`
|
||||
|
|
||||
help: consider restricting type parameter `V`
|
||||
help: consider restricting type parameter `V` with trait `TraitWithAssoc`
|
||||
|
|
||||
LL | pub type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>;
|
||||
| ++++++++++++++++
|
||||
|
|
@ -16,7 +16,7 @@ LL | pub type Foo<V> = impl Trait<V::Assoc>;
|
|||
| ^^^^^ there is an associated type `Assoc` in the trait `TraitWithAssoc`
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
help: consider restricting type parameter `V`
|
||||
help: consider restricting type parameter `V` with trait `TraitWithAssoc`
|
||||
|
|
||||
LL | pub type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>;
|
||||
| ++++++++++++++++
|
||||
|
|
|
|||
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