mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 23:34:48 +00:00
Auto merge of #121804 - GuillaumeGomez:rollup-jh0v3ex, r=GuillaumeGomez
Rollup of 7 pull requests Successful merges: - #119748 (Increase visibility of `join_path` and `split_paths`) - #120820 (Enable CMPXCHG16B, SSE3, SAHF/LAHF and 128-bit Atomics (in nightly) in Windows x64) - #121000 (pattern_analysis: rework how we hide empty private fields) - #121376 (Skip unnecessary comparison with half-open range patterns) - #121596 (Use volatile access instead of `#[used]` for `on_tls_callback`) - #121669 (Count stashed errors again) - #121783 (Emitter cleanups) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
1a1876c979
@ -373,7 +373,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
||||
|
||||
impl<B: WriteBackendMethods> CodegenContext<B> {
|
||||
pub fn create_dcx(&self) -> DiagCtxt {
|
||||
DiagCtxt::with_emitter(Box::new(self.diag_emitter.clone()))
|
||||
DiagCtxt::new(Box::new(self.diag_emitter.clone()))
|
||||
}
|
||||
|
||||
pub fn config(&self, kind: ModuleKind) -> &ModuleConfig {
|
||||
|
@ -21,6 +21,7 @@ use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults};
|
||||
use rustc_data_structures::profiling::{
|
||||
get_resident_set_size, print_time_passes_entry, TimePassesFormat,
|
||||
};
|
||||
use rustc_errors::emitter::stderr_destination;
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::{
|
||||
markdown, ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult,
|
||||
@ -1384,11 +1385,11 @@ fn report_ice(
|
||||
) {
|
||||
let fallback_bundle =
|
||||
rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
|
||||
let emitter = Box::new(rustc_errors::emitter::HumanEmitter::stderr(
|
||||
rustc_errors::ColorConfig::Auto,
|
||||
let emitter = Box::new(rustc_errors::emitter::HumanEmitter::new(
|
||||
stderr_destination(rustc_errors::ColorConfig::Auto),
|
||||
fallback_bundle,
|
||||
));
|
||||
let dcx = rustc_errors::DiagCtxt::with_emitter(emitter);
|
||||
let dcx = rustc_errors::DiagCtxt::new(emitter);
|
||||
|
||||
// a .span_bug or .bug call has already printed what
|
||||
// it wants to print.
|
||||
|
@ -1308,11 +1308,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
|
||||
drop(self);
|
||||
}
|
||||
|
||||
/// Stashes diagnostic for possible later improvement in a different,
|
||||
/// later stage of the compiler. The diagnostic can be accessed with
|
||||
/// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`].
|
||||
pub fn stash(mut self, span: Span, key: StashKey) {
|
||||
self.dcx.stash_diagnostic(span, key, self.take_diag());
|
||||
/// See `DiagCtxt::stash_diagnostic` for details.
|
||||
pub fn stash(mut self, span: Span, key: StashKey) -> Option<ErrorGuaranteed> {
|
||||
self.dcx.stash_diagnostic(span, key, self.take_diag())
|
||||
}
|
||||
|
||||
/// Delay emission of this diagnostic as a bug.
|
||||
|
@ -21,12 +21,11 @@ use crate::{
|
||||
FluentBundle, LazyFallbackBundle, Level, MultiSpan, Subdiag, SubstitutionHighlight,
|
||||
SuggestionStyle, TerminalUrl,
|
||||
};
|
||||
use rustc_lint_defs::pluralize;
|
||||
|
||||
use derive_setters::Setters;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::sync::{DynSend, IntoDynSyncSend, Lrc};
|
||||
use rustc_error_messages::{FluentArgs, SpanLabel};
|
||||
use rustc_lint_defs::pluralize;
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::{max, min, Reverse};
|
||||
@ -35,7 +34,7 @@ use std::io::prelude::*;
|
||||
use std::io::{self, IsTerminal};
|
||||
use std::iter;
|
||||
use std::path::Path;
|
||||
use termcolor::{Ansi, Buffer, BufferWriter, ColorChoice, ColorSpec, StandardStream};
|
||||
use termcolor::{Buffer, BufferWriter, ColorChoice, ColorSpec, StandardStream};
|
||||
use termcolor::{Color, WriteColor};
|
||||
|
||||
/// Default column width, used in tests and when terminal dimensions cannot be determined.
|
||||
@ -58,18 +57,6 @@ impl HumanReadableErrorType {
|
||||
HumanReadableErrorType::AnnotateSnippet(cc) => (false, cc),
|
||||
}
|
||||
}
|
||||
pub fn new_emitter(
|
||||
self,
|
||||
mut dst: Box<dyn WriteColor + Send>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
) -> HumanEmitter {
|
||||
let (short, color_config) = self.unzip();
|
||||
let color = color_config.suggests_using_colors();
|
||||
if !dst.supports_color() && color {
|
||||
dst = Box::new(Ansi::new(dst));
|
||||
}
|
||||
HumanEmitter::new(dst, fallback_bundle).short_message(short)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@ -130,8 +117,8 @@ impl Margin {
|
||||
fn was_cut_right(&self, line_len: usize) -> bool {
|
||||
let right =
|
||||
if self.computed_right == self.span_right || self.computed_right == self.label_right {
|
||||
// Account for the "..." padding given above. Otherwise we end up with code lines that
|
||||
// do fit but end in "..." as if they were trimmed.
|
||||
// Account for the "..." padding given above. Otherwise we end up with code lines
|
||||
// that do fit but end in "..." as if they were trimmed.
|
||||
self.computed_right - 6
|
||||
} else {
|
||||
self.computed_right
|
||||
@ -628,12 +615,6 @@ impl ColorConfig {
|
||||
ColorConfig::Auto => ColorChoice::Never,
|
||||
}
|
||||
}
|
||||
fn suggests_using_colors(self) -> bool {
|
||||
match self {
|
||||
ColorConfig::Always | ColorConfig::Auto => true,
|
||||
ColorConfig::Never => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short`
|
||||
@ -657,19 +638,14 @@ pub struct HumanEmitter {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileWithAnnotatedLines {
|
||||
pub file: Lrc<SourceFile>,
|
||||
pub lines: Vec<Line>,
|
||||
pub(crate) struct FileWithAnnotatedLines {
|
||||
pub(crate) file: Lrc<SourceFile>,
|
||||
pub(crate) lines: Vec<Line>,
|
||||
multiline_depth: usize,
|
||||
}
|
||||
|
||||
impl HumanEmitter {
|
||||
pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> HumanEmitter {
|
||||
let dst = from_stderr(color_config);
|
||||
Self::create(dst, fallback_bundle)
|
||||
}
|
||||
|
||||
fn create(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter {
|
||||
pub fn new(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter {
|
||||
HumanEmitter {
|
||||
dst: IntoDynSyncSend(dst),
|
||||
sm: None,
|
||||
@ -686,13 +662,6 @@ impl HumanEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
dst: Box<dyn WriteColor + Send>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
) -> HumanEmitter {
|
||||
Self::create(dst, fallback_bundle)
|
||||
}
|
||||
|
||||
fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> {
|
||||
if self.ui_testing {
|
||||
Cow::Borrowed(ANONYMIZED_LINE_NUM)
|
||||
@ -724,8 +693,9 @@ impl HumanEmitter {
|
||||
.skip(left)
|
||||
.take_while(|ch| {
|
||||
// Make sure that the trimming on the right will fall within the terminal width.
|
||||
// FIXME: `unicode_width` sometimes disagrees with terminals on how wide a `char` is.
|
||||
// For now, just accept that sometimes the code line will be longer than desired.
|
||||
// FIXME: `unicode_width` sometimes disagrees with terminals on how wide a `char`
|
||||
// is. For now, just accept that sometimes the code line will be longer than
|
||||
// desired.
|
||||
let next = unicode_width::UnicodeWidthChar::width(*ch).unwrap_or(1);
|
||||
if taken + next > right - left {
|
||||
return false;
|
||||
@ -2228,8 +2198,8 @@ impl HumanEmitter {
|
||||
buffer.puts(*row_num - 1, max_line_num_len + 3, &line, Style::NoStyle);
|
||||
*row_num += 1;
|
||||
}
|
||||
// If the last line is exactly equal to the line we need to add, we can skip both of them.
|
||||
// This allows us to avoid output like the following:
|
||||
// If the last line is exactly equal to the line we need to add, we can skip both of
|
||||
// them. This allows us to avoid output like the following:
|
||||
// 2 - &
|
||||
// 2 + if true { true } else { false }
|
||||
// 3 - if true { true } else { false }
|
||||
@ -2586,6 +2556,7 @@ fn num_overlap(
|
||||
let extra = if inclusive { 1 } else { 0 };
|
||||
(b_start..b_end + extra).contains(&a_start) || (a_start..a_end + extra).contains(&b_start)
|
||||
}
|
||||
|
||||
fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool {
|
||||
num_overlap(
|
||||
a1.start_col.display,
|
||||
@ -2632,7 +2603,7 @@ fn emit_to_destination(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub type Destination = Box<(dyn WriteColor + Send)>;
|
||||
pub type Destination = Box<dyn WriteColor + Send>;
|
||||
|
||||
struct Buffy {
|
||||
buffer_writer: BufferWriter,
|
||||
@ -2674,7 +2645,7 @@ impl WriteColor for Buffy {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_stderr(color: ColorConfig) -> Destination {
|
||||
pub fn stderr_destination(color: ColorConfig) -> Destination {
|
||||
let choice = color.to_color_choice();
|
||||
// On Windows we'll be performing global synchronization on the entire
|
||||
// system for emitting rustc errors, so there's no need to buffer
|
||||
|
@ -23,9 +23,11 @@ impl<'args> TranslateError<'args> {
|
||||
pub fn message(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self {
|
||||
Self::One { id, args, kind: TranslateErrorKind::MessageMissing }
|
||||
}
|
||||
|
||||
pub fn primary(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self {
|
||||
Self::One { id, args, kind: TranslateErrorKind::PrimaryBundleMissing }
|
||||
}
|
||||
|
||||
pub fn attribute(
|
||||
id: &'args Cow<'args, str>,
|
||||
args: &'args FluentArgs<'args>,
|
||||
@ -33,6 +35,7 @@ impl<'args> TranslateError<'args> {
|
||||
) -> Self {
|
||||
Self::One { id, args, kind: TranslateErrorKind::AttributeMissing { attr } }
|
||||
}
|
||||
|
||||
pub fn value(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self {
|
||||
Self::One { id, args, kind: TranslateErrorKind::ValueMissing }
|
||||
}
|
||||
|
@ -9,42 +9,49 @@
|
||||
|
||||
// FIXME: spec the JSON output properly.
|
||||
|
||||
use rustc_span::source_map::{FilePathMapping, SourceMap};
|
||||
use termcolor::{ColorSpec, WriteColor};
|
||||
|
||||
use crate::emitter::{should_show_source_code, Emitter, HumanReadableErrorType};
|
||||
use crate::emitter::{
|
||||
should_show_source_code, ColorConfig, Destination, Emitter, HumanEmitter,
|
||||
HumanReadableErrorType,
|
||||
};
|
||||
use crate::registry::Registry;
|
||||
use crate::translation::{to_fluent_args, Translate};
|
||||
use crate::{
|
||||
diagnostic::IsLint, CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel,
|
||||
Subdiag, TerminalUrl,
|
||||
};
|
||||
use rustc_lint_defs::Applicability;
|
||||
|
||||
use derive_setters::Setters;
|
||||
use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
|
||||
use rustc_error_messages::FluentArgs;
|
||||
use rustc_lint_defs::Applicability;
|
||||
use rustc_span::hygiene::ExpnData;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::Span;
|
||||
use serde::Serialize;
|
||||
use std::error::Report;
|
||||
use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::vec;
|
||||
|
||||
use serde::Serialize;
|
||||
use termcolor::{ColorSpec, WriteColor};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[derive(Setters)]
|
||||
pub struct JsonEmitter {
|
||||
#[setters(skip)]
|
||||
dst: IntoDynSyncSend<Box<dyn Write + Send>>,
|
||||
registry: Option<Registry>,
|
||||
#[setters(skip)]
|
||||
sm: Lrc<SourceMap>,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
#[setters(skip)]
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
#[setters(skip)]
|
||||
pretty: bool,
|
||||
ui_testing: bool,
|
||||
ignored_directories_in_source_blocks: Vec<String>,
|
||||
#[setters(skip)]
|
||||
json_rendered: HumanReadableErrorType,
|
||||
diagnostic_width: Option<usize>,
|
||||
macro_backtrace: bool,
|
||||
@ -53,98 +60,30 @@ pub struct JsonEmitter {
|
||||
}
|
||||
|
||||
impl JsonEmitter {
|
||||
pub fn stderr(
|
||||
registry: Option<Registry>,
|
||||
source_map: Lrc<SourceMap>,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
pretty: bool,
|
||||
json_rendered: HumanReadableErrorType,
|
||||
diagnostic_width: Option<usize>,
|
||||
macro_backtrace: bool,
|
||||
track_diagnostics: bool,
|
||||
terminal_url: TerminalUrl,
|
||||
) -> JsonEmitter {
|
||||
JsonEmitter {
|
||||
dst: IntoDynSyncSend(Box::new(io::BufWriter::new(io::stderr()))),
|
||||
registry,
|
||||
sm: source_map,
|
||||
fluent_bundle,
|
||||
fallback_bundle,
|
||||
pretty,
|
||||
ui_testing: false,
|
||||
ignored_directories_in_source_blocks: Vec::new(),
|
||||
json_rendered,
|
||||
diagnostic_width,
|
||||
macro_backtrace,
|
||||
track_diagnostics,
|
||||
terminal_url,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic(
|
||||
pretty: bool,
|
||||
json_rendered: HumanReadableErrorType,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
diagnostic_width: Option<usize>,
|
||||
macro_backtrace: bool,
|
||||
track_diagnostics: bool,
|
||||
terminal_url: TerminalUrl,
|
||||
) -> JsonEmitter {
|
||||
let file_path_mapping = FilePathMapping::empty();
|
||||
JsonEmitter::stderr(
|
||||
None,
|
||||
Lrc::new(SourceMap::new(file_path_mapping)),
|
||||
fluent_bundle,
|
||||
fallback_bundle,
|
||||
pretty,
|
||||
json_rendered,
|
||||
diagnostic_width,
|
||||
macro_backtrace,
|
||||
track_diagnostics,
|
||||
terminal_url,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
dst: Box<dyn Write + Send>,
|
||||
registry: Option<Registry>,
|
||||
source_map: Lrc<SourceMap>,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
sm: Lrc<SourceMap>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
pretty: bool,
|
||||
json_rendered: HumanReadableErrorType,
|
||||
diagnostic_width: Option<usize>,
|
||||
macro_backtrace: bool,
|
||||
track_diagnostics: bool,
|
||||
terminal_url: TerminalUrl,
|
||||
) -> JsonEmitter {
|
||||
JsonEmitter {
|
||||
dst: IntoDynSyncSend(dst),
|
||||
registry,
|
||||
sm: source_map,
|
||||
fluent_bundle,
|
||||
registry: None,
|
||||
sm,
|
||||
fluent_bundle: None,
|
||||
fallback_bundle,
|
||||
pretty,
|
||||
ui_testing: false,
|
||||
ignored_directories_in_source_blocks: Vec::new(),
|
||||
json_rendered,
|
||||
diagnostic_width,
|
||||
macro_backtrace,
|
||||
track_diagnostics,
|
||||
terminal_url,
|
||||
diagnostic_width: None,
|
||||
macro_backtrace: false,
|
||||
track_diagnostics: false,
|
||||
terminal_url: TerminalUrl::No,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui_testing(self, ui_testing: bool) -> Self {
|
||||
Self { ui_testing, ..self }
|
||||
}
|
||||
|
||||
pub fn ignored_directories_in_source_blocks(self, value: Vec<String>) -> Self {
|
||||
Self { ignored_directories_in_source_blocks: value, ..self }
|
||||
}
|
||||
|
||||
fn emit(&mut self, val: EmitTyped<'_>) -> io::Result<()> {
|
||||
if self.pretty {
|
||||
serde_json::to_writer_pretty(&mut *self.dst, &val)?
|
||||
@ -162,7 +101,7 @@ enum EmitTyped<'a> {
|
||||
Diagnostic(Diagnostic),
|
||||
Artifact(ArtifactNotification<'a>),
|
||||
FutureIncompat(FutureIncompatReport<'a>),
|
||||
UnusedExtern(UnusedExterns<'a, 'a, 'a>),
|
||||
UnusedExtern(UnusedExterns<'a>),
|
||||
}
|
||||
|
||||
impl Translate for JsonEmitter {
|
||||
@ -332,14 +271,15 @@ struct FutureIncompatReport<'a> {
|
||||
// We could unify this struct the one in rustdoc but they have different
|
||||
// ownership semantics, so doing so would create wasteful allocations.
|
||||
#[derive(Serialize)]
|
||||
struct UnusedExterns<'a, 'b, 'c> {
|
||||
struct UnusedExterns<'a> {
|
||||
/// The severity level of the unused dependencies lint
|
||||
lint_level: &'a str,
|
||||
/// List of unused externs by their names.
|
||||
unused_extern_names: &'b [&'c str],
|
||||
unused_extern_names: &'a [&'a str],
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
/// Converts from `rustc_errors::DiagInner` to `Diagnostic`.
|
||||
fn from_errors_diagnostic(diag: crate::DiagInner, je: &JsonEmitter) -> Diagnostic {
|
||||
let args = to_fluent_args(diag.args.iter());
|
||||
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
|
||||
@ -405,9 +345,14 @@ impl Diagnostic {
|
||||
.collect();
|
||||
|
||||
let buf = BufWriter::default();
|
||||
let output = buf.clone();
|
||||
je.json_rendered
|
||||
.new_emitter(Box::new(buf), je.fallback_bundle.clone())
|
||||
let mut dst: Destination = Box::new(buf.clone());
|
||||
let (short, color_config) = je.json_rendered.unzip();
|
||||
match color_config {
|
||||
ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)),
|
||||
ColorConfig::Never => {}
|
||||
}
|
||||
HumanEmitter::new(dst, je.fallback_bundle.clone())
|
||||
.short_message(short)
|
||||
.sm(Some(je.sm.clone()))
|
||||
.fluent_bundle(je.fluent_bundle.clone())
|
||||
.diagnostic_width(je.diagnostic_width)
|
||||
@ -417,8 +362,8 @@ impl Diagnostic {
|
||||
.ui_testing(je.ui_testing)
|
||||
.ignored_directories_in_source_blocks(je.ignored_directories_in_source_blocks.clone())
|
||||
.emit_diagnostic(diag);
|
||||
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
|
||||
let output = String::from_utf8(output).unwrap();
|
||||
let buf = Arc::try_unwrap(buf.0).unwrap().into_inner().unwrap();
|
||||
let buf = String::from_utf8(buf).unwrap();
|
||||
|
||||
Diagnostic {
|
||||
message: translated_message.to_string(),
|
||||
@ -426,7 +371,7 @@ impl Diagnostic {
|
||||
level,
|
||||
spans,
|
||||
children,
|
||||
rendered: Some(output),
|
||||
rendered: Some(buf),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
|
||||
use crate::emitter::ColorConfig;
|
||||
use crate::DiagCtxt;
|
||||
use rustc_span::source_map::FilePathMapping;
|
||||
use rustc_span::BytePos;
|
||||
|
||||
use std::str;
|
||||
@ -48,20 +48,14 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
|
||||
let output = Arc::new(Mutex::new(Vec::new()));
|
||||
let je = JsonEmitter::new(
|
||||
Box::new(Shared { data: output.clone() }),
|
||||
None,
|
||||
sm,
|
||||
None,
|
||||
fallback_bundle,
|
||||
true,
|
||||
true, // pretty
|
||||
HumanReadableErrorType::Short(ColorConfig::Never),
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
TerminalUrl::No,
|
||||
);
|
||||
|
||||
let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));
|
||||
let dcx = DiagCtxt::with_emitter(Box::new(je));
|
||||
let dcx = DiagCtxt::new(Box::new(je));
|
||||
dcx.span_err(span, "foo");
|
||||
|
||||
let bytes = output.lock().unwrap();
|
||||
|
@ -59,11 +59,11 @@ pub use snippet::Style;
|
||||
// See https://github.com/rust-lang/rust/pull/115393.
|
||||
pub use termcolor::{Color, ColorSpec, WriteColor};
|
||||
|
||||
use emitter::{is_case_difference, DynEmitter, Emitter, HumanEmitter};
|
||||
use emitter::{is_case_difference, DynEmitter, Emitter};
|
||||
use registry::Registry;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::stable_hasher::{Hash128, StableHasher};
|
||||
use rustc_data_structures::sync::{Lock, Lrc};
|
||||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_data_structures::AtomicRef;
|
||||
use rustc_lint_defs::LintExpectationId;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
@ -217,10 +217,10 @@ impl CodeSuggestion {
|
||||
|
||||
use rustc_span::{CharPos, Pos};
|
||||
|
||||
/// Extracts a substring from the provided `line_opt` based on the specified low and high indices,
|
||||
/// appends it to the given buffer `buf`, and returns the count of newline characters in the substring
|
||||
/// for accurate highlighting.
|
||||
/// If `line_opt` is `None`, a newline character is appended to the buffer, and 0 is returned.
|
||||
/// Extracts a substring from the provided `line_opt` based on the specified low and high
|
||||
/// indices, appends it to the given buffer `buf`, and returns the count of newline
|
||||
/// characters in the substring for accurate highlighting. If `line_opt` is `None`, a
|
||||
/// newline character is appended to the buffer, and 0 is returned.
|
||||
///
|
||||
/// ## Returns
|
||||
///
|
||||
@ -434,10 +434,6 @@ struct DiagCtxtInner {
|
||||
/// The delayed bugs and their error guarantees.
|
||||
delayed_bugs: Vec<(DelayedDiagInner, ErrorGuaranteed)>,
|
||||
|
||||
/// The number of stashed errors. Unlike the other counts, this can go up
|
||||
/// and down, so it doesn't guarantee anything.
|
||||
stashed_err_count: usize,
|
||||
|
||||
/// The error count shown to the user at the end.
|
||||
deduplicated_err_count: usize,
|
||||
/// The warning count shown to the user at the end.
|
||||
@ -475,7 +471,7 @@ struct DiagCtxtInner {
|
||||
/// add more information). All stashed diagnostics must be emitted with
|
||||
/// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
|
||||
/// otherwise an assertion failure will occur.
|
||||
stashed_diagnostics: FxIndexMap<(Span, StashKey), DiagInner>,
|
||||
stashed_diagnostics: FxIndexMap<(Span, StashKey), (DiagInner, Option<ErrorGuaranteed>)>,
|
||||
|
||||
future_breakage_diagnostics: Vec<DiagInner>,
|
||||
|
||||
@ -486,8 +482,8 @@ struct DiagCtxtInner {
|
||||
/// have been converted.
|
||||
check_unstable_expect_diagnostics: bool,
|
||||
|
||||
/// Expected [`DiagInner`][struct@diagnostic::DiagInner]s store a [`LintExpectationId`] as part of
|
||||
/// the lint level. [`LintExpectationId`]s created early during the compilation
|
||||
/// Expected [`DiagInner`][struct@diagnostic::DiagInner]s store a [`LintExpectationId`] as part
|
||||
/// of the lint level. [`LintExpectationId`]s created early during the compilation
|
||||
/// (before `HirId`s have been defined) are not stable and can therefore not be
|
||||
/// stored on disk. This buffer stores these diagnostics until the ID has been
|
||||
/// replaced by a stable [`LintExpectationId`]. The [`DiagInner`][struct@diagnostic::DiagInner]s
|
||||
@ -559,10 +555,18 @@ pub struct DiagCtxtFlags {
|
||||
|
||||
impl Drop for DiagCtxtInner {
|
||||
fn drop(&mut self) {
|
||||
// Any stashed diagnostics should have been handled by
|
||||
// `emit_stashed_diagnostics` by now.
|
||||
assert!(self.stashed_diagnostics.is_empty());
|
||||
// For tools using `interface::run_compiler` (e.g. rustc, rustdoc)
|
||||
// stashed diagnostics will have already been emitted. But for others
|
||||
// that don't use `interface::run_compiler` (e.g. rustfmt, some clippy
|
||||
// lints) this fallback is necessary.
|
||||
//
|
||||
// Important: it is sound to produce an `ErrorGuaranteed` when stashing
|
||||
// errors because they are guaranteed to be emitted here or earlier.
|
||||
self.emit_stashed_diagnostics();
|
||||
|
||||
// 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()
|
||||
}
|
||||
@ -586,13 +590,6 @@ impl Drop for DiagCtxtInner {
|
||||
}
|
||||
|
||||
impl DiagCtxt {
|
||||
pub fn with_tty_emitter(
|
||||
sm: Option<Lrc<SourceMap>>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
) -> Self {
|
||||
let emitter = Box::new(HumanEmitter::stderr(ColorConfig::Auto, fallback_bundle).sm(sm));
|
||||
Self::with_emitter(emitter)
|
||||
}
|
||||
pub fn disable_warnings(mut self) -> Self {
|
||||
self.inner.get_mut().flags.can_emit_warnings = false;
|
||||
self
|
||||
@ -608,14 +605,13 @@ impl DiagCtxt {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_emitter(emitter: Box<DynEmitter>) -> Self {
|
||||
pub fn new(emitter: Box<DynEmitter>) -> Self {
|
||||
Self {
|
||||
inner: Lock::new(DiagCtxtInner {
|
||||
flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() },
|
||||
err_guars: Vec::new(),
|
||||
lint_err_guars: Vec::new(),
|
||||
delayed_bugs: Vec::new(),
|
||||
stashed_err_count: 0,
|
||||
deduplicated_err_count: 0,
|
||||
deduplicated_warn_count: 0,
|
||||
emitter,
|
||||
@ -676,7 +672,6 @@ impl DiagCtxt {
|
||||
err_guars,
|
||||
lint_err_guars,
|
||||
delayed_bugs,
|
||||
stashed_err_count,
|
||||
deduplicated_err_count,
|
||||
deduplicated_warn_count,
|
||||
emitter: _,
|
||||
@ -699,7 +694,6 @@ impl DiagCtxt {
|
||||
*err_guars = Default::default();
|
||||
*lint_err_guars = Default::default();
|
||||
*delayed_bugs = Default::default();
|
||||
*stashed_err_count = 0;
|
||||
*deduplicated_err_count = 0;
|
||||
*deduplicated_warn_count = 0;
|
||||
*must_produce_diag = false;
|
||||
@ -715,39 +709,111 @@ impl DiagCtxt {
|
||||
*fulfilled_expectations = Default::default();
|
||||
}
|
||||
|
||||
/// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key.
|
||||
/// Retrieve a stashed diagnostic with `steal_diagnostic`.
|
||||
pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: DiagInner) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
||||
let key = (span.with_parent(None), key);
|
||||
|
||||
if diag.is_error() {
|
||||
if diag.is_lint.is_none() {
|
||||
inner.stashed_err_count += 1;
|
||||
}
|
||||
}
|
||||
/// Stashes a diagnostic for possible later improvement in a different,
|
||||
/// later stage of the compiler. Possible actions depend on the diagnostic
|
||||
/// level:
|
||||
/// - Level::Error: immediately counted as an error that has occurred, because it
|
||||
/// is guaranteed to be emitted eventually. Can be later accessed with the
|
||||
/// provided `span` and `key` through
|
||||
/// [`DiagCtxt::try_steal_modify_and_emit_err`] or
|
||||
/// [`DiagCtxt::try_steal_replace_and_emit_err`]. These do not allow
|
||||
/// cancellation or downgrading of the error. Returns
|
||||
/// `Some(ErrorGuaranteed)`.
|
||||
/// - Level::Warning and lower (i.e. !is_error()): can be accessed with the
|
||||
/// provided `span` and `key` through [`DiagCtxt::steal_non_err()`]. This
|
||||
/// allows cancelling and downgrading of the diagnostic. Returns `None`.
|
||||
/// - Others: not allowed, will trigger a panic.
|
||||
pub fn stash_diagnostic(
|
||||
&self,
|
||||
span: Span,
|
||||
key: StashKey,
|
||||
diag: DiagInner,
|
||||
) -> Option<ErrorGuaranteed> {
|
||||
let guar = if diag.level() == Level::Error {
|
||||
// This `unchecked_error_guaranteed` is valid. It is where the
|
||||
// `ErrorGuaranteed` for stashed errors originates. See
|
||||
// `DiagCtxtInner::drop`.
|
||||
#[allow(deprecated)]
|
||||
Some(ErrorGuaranteed::unchecked_error_guaranteed())
|
||||
} else if !diag.is_error() {
|
||||
None
|
||||
} else {
|
||||
self.span_bug(span, format!("invalid level in `stash_diagnostic`: {}", diag.level));
|
||||
};
|
||||
|
||||
// FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
|
||||
// if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
|
||||
// See the PR for a discussion.
|
||||
inner.stashed_diagnostics.insert(key, diag);
|
||||
let key = (span.with_parent(None), key);
|
||||
self.inner.borrow_mut().stashed_diagnostics.insert(key, (diag, guar));
|
||||
|
||||
guar
|
||||
}
|
||||
|
||||
/// Steal a previously stashed diagnostic with the given `Span` and [`StashKey`] as the key.
|
||||
pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<Diag<'_, ()>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
/// Steal a previously stashed non-error diagnostic with the given `Span`
|
||||
/// and [`StashKey`] as the key. Panics if the found diagnostic is an
|
||||
/// error.
|
||||
pub fn steal_non_err(&self, span: Span, key: StashKey) -> Option<Diag<'_, ()>> {
|
||||
let key = (span.with_parent(None), key);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
let diag = inner.stashed_diagnostics.swap_remove(&key)?;
|
||||
if diag.is_error() {
|
||||
if diag.is_lint.is_none() {
|
||||
inner.stashed_err_count -= 1;
|
||||
}
|
||||
}
|
||||
let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key)?;
|
||||
assert!(!diag.is_error());
|
||||
assert!(guar.is_none());
|
||||
Some(Diag::new_diagnostic(self, diag))
|
||||
}
|
||||
|
||||
/// Steals a previously stashed error with the given `Span` and
|
||||
/// [`StashKey`] as the key, modifies it, and emits it. Returns `None` if
|
||||
/// no matching diagnostic is found. Panics if the found diagnostic's level
|
||||
/// isn't `Level::Error`.
|
||||
pub fn try_steal_modify_and_emit_err<F>(
|
||||
&self,
|
||||
span: Span,
|
||||
key: StashKey,
|
||||
mut modify_err: F,
|
||||
) -> Option<ErrorGuaranteed>
|
||||
where
|
||||
F: FnMut(&mut Diag<'_>),
|
||||
{
|
||||
let key = (span.with_parent(None), key);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
let err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key);
|
||||
err.map(|(err, guar)| {
|
||||
// The use of `::<ErrorGuaranteed>` is safe because level is `Level::Error`.
|
||||
assert_eq!(err.level, Level::Error);
|
||||
assert!(guar.is_some());
|
||||
let mut err = Diag::<ErrorGuaranteed>::new_diagnostic(self, err);
|
||||
modify_err(&mut err);
|
||||
assert_eq!(err.level, Level::Error);
|
||||
err.emit()
|
||||
})
|
||||
}
|
||||
|
||||
/// Steals a previously stashed error with the given `Span` and
|
||||
/// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
|
||||
/// Panics if the found diagnostic's level isn't `Level::Error`.
|
||||
pub fn try_steal_replace_and_emit_err(
|
||||
&self,
|
||||
span: Span,
|
||||
key: StashKey,
|
||||
new_err: Diag<'_>,
|
||||
) -> ErrorGuaranteed {
|
||||
let key = (span.with_parent(None), key);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
let old_err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key);
|
||||
match old_err {
|
||||
Some((old_err, guar)) => {
|
||||
assert_eq!(old_err.level, Level::Error);
|
||||
assert!(guar.is_some());
|
||||
// Because `old_err` has already been counted, it can only be
|
||||
// safely cancelled because the `new_err` supplants it.
|
||||
Diag::<ErrorGuaranteed>::new_diagnostic(self, old_err).cancel();
|
||||
}
|
||||
None => {}
|
||||
};
|
||||
new_err.emit()
|
||||
}
|
||||
|
||||
pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool {
|
||||
self.inner.borrow().stashed_diagnostics.get(&(span.with_parent(None), key)).is_some()
|
||||
}
|
||||
@ -757,41 +823,40 @@ impl DiagCtxt {
|
||||
self.inner.borrow_mut().emit_stashed_diagnostics()
|
||||
}
|
||||
|
||||
/// This excludes lint errors, delayed bugs and stashed errors.
|
||||
/// This excludes lint errors, and delayed bugs.
|
||||
#[inline]
|
||||
pub fn err_count_excluding_lint_errs(&self) -> usize {
|
||||
self.inner.borrow().err_guars.len()
|
||||
let inner = self.inner.borrow();
|
||||
inner.err_guars.len()
|
||||
+ inner
|
||||
.stashed_diagnostics
|
||||
.values()
|
||||
.filter(|(diag, guar)| guar.is_some() && diag.is_lint.is_none())
|
||||
.count()
|
||||
}
|
||||
|
||||
/// This excludes delayed bugs and stashed errors.
|
||||
/// This excludes delayed bugs.
|
||||
#[inline]
|
||||
pub fn err_count(&self) -> usize {
|
||||
let inner = self.inner.borrow();
|
||||
inner.err_guars.len() + inner.lint_err_guars.len()
|
||||
inner.err_guars.len()
|
||||
+ inner.lint_err_guars.len()
|
||||
+ inner.stashed_diagnostics.values().filter(|(_diag, guar)| guar.is_some()).count()
|
||||
}
|
||||
|
||||
/// This excludes normal errors, lint errors, and delayed bugs. Unless
|
||||
/// absolutely necessary, avoid using this. It's dubious because stashed
|
||||
/// errors can later be cancelled, so the presence of a stashed error at
|
||||
/// some point of time doesn't guarantee anything -- there are no
|
||||
/// `ErrorGuaranteed`s here.
|
||||
pub fn stashed_err_count(&self) -> usize {
|
||||
self.inner.borrow().stashed_err_count
|
||||
}
|
||||
|
||||
/// This excludes lint errors, delayed bugs, and stashed errors. Unless
|
||||
/// absolutely necessary, prefer `has_errors` to this method.
|
||||
/// This excludes lint errors and delayed bugs. Unless absolutely
|
||||
/// necessary, prefer `has_errors` to this method.
|
||||
pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors_excluding_lint_errors()
|
||||
}
|
||||
|
||||
/// This excludes delayed bugs and stashed errors.
|
||||
/// This excludes delayed bugs.
|
||||
pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors()
|
||||
}
|
||||
|
||||
/// This excludes stashed errors. Unless absolutely necessary, prefer
|
||||
/// `has_errors` to this method.
|
||||
/// This excludes nothing. Unless absolutely necessary, prefer `has_errors`
|
||||
/// to this method.
|
||||
pub fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors_or_delayed_bugs()
|
||||
}
|
||||
@ -876,10 +941,10 @@ impl DiagCtxt {
|
||||
}
|
||||
}
|
||||
|
||||
/// This excludes delayed bugs and stashed errors. Used for early aborts
|
||||
/// after errors occurred -- e.g. because continuing in the face of errors is
|
||||
/// likely to lead to bad results, such as spurious/uninteresting
|
||||
/// additional errors -- when returning an error `Result` is difficult.
|
||||
/// This excludes delayed bugs. Used for early aborts after errors occurred
|
||||
/// -- e.g. because continuing in the face of errors is likely to lead to
|
||||
/// bad results, such as spurious/uninteresting additional errors -- when
|
||||
/// returning an error `Result` is difficult.
|
||||
pub fn abort_if_errors(&self) {
|
||||
if self.has_errors().is_some() {
|
||||
FatalError.raise();
|
||||
@ -963,7 +1028,7 @@ impl DiagCtxt {
|
||||
inner
|
||||
.stashed_diagnostics
|
||||
.values_mut()
|
||||
.for_each(|diag| diag.update_unstable_expectation_id(unstable_to_stable));
|
||||
.for_each(|(diag, _guar)| diag.update_unstable_expectation_id(unstable_to_stable));
|
||||
inner
|
||||
.future_breakage_diagnostics
|
||||
.iter_mut()
|
||||
@ -1270,12 +1335,8 @@ impl DiagCtxtInner {
|
||||
fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
|
||||
let mut guar = None;
|
||||
let has_errors = !self.err_guars.is_empty();
|
||||
for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
|
||||
if diag.is_error() {
|
||||
if diag.is_lint.is_none() {
|
||||
self.stashed_err_count -= 1;
|
||||
}
|
||||
} else {
|
||||
for (_, (diag, _guar)) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
|
||||
if !diag.is_error() {
|
||||
// Unless they're forced, don't flush stashed warnings when
|
||||
// there are errors, to avoid causing warning overload. The
|
||||
// stash would've been stolen already if it were important.
|
||||
@ -1334,7 +1395,8 @@ impl DiagCtxtInner {
|
||||
} else {
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
// This `unchecked_error_guaranteed` is valid. It is where the
|
||||
// `ErrorGuaranteed` for delayed bugs originates.
|
||||
// `ErrorGuaranteed` for delayed bugs originates. See
|
||||
// `DiagCtxtInner::drop`.
|
||||
#[allow(deprecated)]
|
||||
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
|
||||
self.delayed_bugs
|
||||
@ -1446,11 +1508,31 @@ impl DiagCtxtInner {
|
||||
}
|
||||
|
||||
fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.err_guars.get(0).copied()
|
||||
self.err_guars.get(0).copied().or_else(|| {
|
||||
if let Some((_diag, guar)) = self
|
||||
.stashed_diagnostics
|
||||
.values()
|
||||
.find(|(diag, guar)| guar.is_some() && diag.is_lint.is_none())
|
||||
{
|
||||
*guar
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn has_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.has_errors_excluding_lint_errors().or_else(|| self.lint_err_guars.get(0).copied())
|
||||
self.err_guars.get(0).copied().or_else(|| self.lint_err_guars.get(0).copied()).or_else(
|
||||
|| {
|
||||
if let Some((_diag, guar)) =
|
||||
self.stashed_diagnostics.values().find(|(_diag, guar)| guar.is_some())
|
||||
{
|
||||
*guar
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
|
@ -33,7 +33,7 @@ fn create_test_handler() -> (DiagCtxt, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
|
||||
let emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle)
|
||||
.sm(Some(source_map.clone()))
|
||||
.diagnostic_width(Some(140));
|
||||
let dcx = DiagCtxt::with_emitter(Box::new(emitter));
|
||||
let dcx = DiagCtxt::new(Box::new(emitter));
|
||||
(dcx, source_map, output)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use rustc_ast::TraitObjectSyntax;
|
||||
use rustc_errors::{codes::*, Diag, EmissionGuarantee, StashKey};
|
||||
use rustc_errors::{codes::*, Diag, EmissionGuarantee, Level, StashKey};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability};
|
||||
@ -237,7 +237,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
}
|
||||
// check if the impl trait that we are considering is a impl of a local trait
|
||||
self.maybe_lint_blanket_trait_impl(self_ty, &mut diag);
|
||||
diag.stash(self_ty.span, StashKey::TraitMissingMethod);
|
||||
match diag.level() {
|
||||
Level::Error => {
|
||||
diag.stash(self_ty.span, StashKey::TraitMissingMethod);
|
||||
}
|
||||
Level::DelayedBug => {
|
||||
diag.emit();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
let msg = "trait objects without an explicit `dyn` are deprecated";
|
||||
tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, msg, |lint| {
|
||||
|
@ -1350,8 +1350,7 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD
|
||||
let ty = tcx.type_of(def_id).instantiate_identity();
|
||||
if ty.references_error() {
|
||||
// If there is already another error, do not emit an error for not using a type parameter.
|
||||
// Without the `stashed_err_count` part this can fail (#120856).
|
||||
assert!(tcx.dcx().has_errors().is_some() || tcx.dcx().stashed_err_count() > 0);
|
||||
assert!(tcx.dcx().has_errors().is_some());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -596,10 +596,11 @@ fn infer_placeholder_type<'a>(
|
||||
// then the user may have written e.g. `const A = 42;`.
|
||||
// In this case, the parser has stashed a diagnostic for
|
||||
// us to improve in typeck so we do that now.
|
||||
match tcx.dcx().steal_diagnostic(span, StashKey::ItemNoType) {
|
||||
Some(mut err) => {
|
||||
let guar = tcx
|
||||
.dcx()
|
||||
.try_steal_modify_and_emit_err(span, StashKey::ItemNoType, |err| {
|
||||
if !ty.references_error() {
|
||||
// Only suggest adding `:` if it was missing (and suggested by parsing diagnostic)
|
||||
// Only suggest adding `:` if it was missing (and suggested by parsing diagnostic).
|
||||
let colon = if span == item_ident.span.shrink_to_hi() { ":" } else { "" };
|
||||
|
||||
// The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
|
||||
@ -622,12 +623,8 @@ fn infer_placeholder_type<'a>(
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
err.emit();
|
||||
// diagnostic stashing loses the information of whether something is a hard error.
|
||||
Ty::new_error_with_message(tcx, span, "ItemNoType is a hard error")
|
||||
}
|
||||
None => {
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
let mut diag = bad_placeholder(tcx, vec![span], kind);
|
||||
|
||||
if !ty.references_error() {
|
||||
@ -645,10 +642,9 @@ fn infer_placeholder_type<'a>(
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ty::new_error(tcx, diag.emit())
|
||||
}
|
||||
}
|
||||
diag.emit()
|
||||
});
|
||||
Ty::new_error(tcx, guar)
|
||||
}
|
||||
|
||||
fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) {
|
||||
|
@ -484,12 +484,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind
|
||||
&& let [segment] = path.segments
|
||||
&& let Some(mut diag) =
|
||||
self.dcx().steal_diagnostic(segment.ident.span, StashKey::CallIntoMethod)
|
||||
{
|
||||
// Try suggesting `foo(a)` -> `a.foo()` if possible.
|
||||
self.suggest_call_as_method(&mut diag, segment, arg_exprs, call_expr, expected);
|
||||
diag.emit();
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
segment.ident.span,
|
||||
StashKey::CallIntoMethod,
|
||||
|err| {
|
||||
// Try suggesting `foo(a)` -> `a.foo()` if possible.
|
||||
self.suggest_call_as_method(
|
||||
err, segment, arg_exprs, call_expr, expected,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
|
||||
@ -601,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// and suggesting the fix if the method probe is successful.
|
||||
fn suggest_call_as_method(
|
||||
&self,
|
||||
diag: &mut Diag<'_, ()>,
|
||||
diag: &mut Diag<'_>,
|
||||
segment: &'tcx hir::PathSegment<'tcx>,
|
||||
arg_exprs: &'tcx [hir::Expr<'tcx>],
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
|
@ -1460,18 +1460,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&& let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
|
||||
{
|
||||
let span = self.tcx.hir().span(hir_id);
|
||||
match self.dcx().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) {
|
||||
Some(mut err) => {
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
span,
|
||||
StashKey::UnderscoreForArrayLengths,
|
||||
|err| {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"consider specifying the array length",
|
||||
array_len,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
err.emit();
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1751,11 +1751,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let (_, diag) =
|
||||
self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No);
|
||||
|
||||
if let Some(mut diag) = diag {
|
||||
if let Some(diag) = diag {
|
||||
if idx == ast_fields.len() - 1 {
|
||||
if remaining_fields.is_empty() {
|
||||
self.suggest_fru_from_range(field, variant, args, &mut diag);
|
||||
diag.emit();
|
||||
self.suggest_fru_from_range_and_emit(field, variant, args, diag);
|
||||
} else {
|
||||
diag.stash(field.span, StashKey::MaybeFruTypo);
|
||||
}
|
||||
@ -1986,20 +1985,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}"));
|
||||
|
||||
if let Some(last) = ast_fields.last() {
|
||||
self.suggest_fru_from_range(last, variant, args, &mut err);
|
||||
self.suggest_fru_from_range_and_emit(last, variant, args, err);
|
||||
} else {
|
||||
err.emit();
|
||||
}
|
||||
|
||||
err.emit();
|
||||
}
|
||||
|
||||
/// If the last field is a range literal, but it isn't supposed to be, then they probably
|
||||
/// meant to use functional update syntax.
|
||||
fn suggest_fru_from_range(
|
||||
fn suggest_fru_from_range_and_emit(
|
||||
&self,
|
||||
last_expr_field: &hir::ExprField<'tcx>,
|
||||
variant: &ty::VariantDef,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
mut err: Diag<'_>,
|
||||
) {
|
||||
// I don't use 'is_range_literal' because only double-sided, half-open ranges count.
|
||||
if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [range_start, range_end], _) =
|
||||
@ -2012,16 +2011,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.map(|adt| adt.did())
|
||||
!= range_def_id
|
||||
{
|
||||
// Suppress any range expr type mismatches
|
||||
if let Some(diag) =
|
||||
self.dcx().steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo)
|
||||
{
|
||||
diag.delay_as_bug();
|
||||
}
|
||||
|
||||
// Use a (somewhat arbitrary) filtering heuristic to avoid printing
|
||||
// expressions that are either too long, or have control character
|
||||
//such as newlines in them.
|
||||
// such as newlines in them.
|
||||
let expr = self
|
||||
.tcx
|
||||
.sess
|
||||
@ -2043,6 +2035,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.dcx(),
|
||||
TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr },
|
||||
);
|
||||
|
||||
// Suppress any range expr type mismatches
|
||||
self.dcx().try_steal_replace_and_emit_err(
|
||||
last_expr_field.span,
|
||||
StashKey::MaybeFruTypo,
|
||||
err,
|
||||
);
|
||||
} else {
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -848,11 +848,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id)
|
||||
.map(|r| {
|
||||
// lint bare trait if the method is found in the trait
|
||||
if span.edition().at_least_rust_2021()
|
||||
&& let Some(diag) =
|
||||
self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod)
|
||||
{
|
||||
diag.emit();
|
||||
if span.edition().at_least_rust_2021() {
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
qself.span,
|
||||
StashKey::TraitMissingMethod,
|
||||
|_err| {},
|
||||
);
|
||||
}
|
||||
r
|
||||
})
|
||||
@ -879,17 +880,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
// emit or cancel the diagnostic for bare traits
|
||||
if span.edition().at_least_rust_2021()
|
||||
&& let Some(diag) =
|
||||
self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod)
|
||||
{
|
||||
if trait_missing_method {
|
||||
// cancel the diag for bare traits when meeting `MyTrait::missing_method`
|
||||
diag.cancel();
|
||||
} else {
|
||||
diag.emit();
|
||||
}
|
||||
// Emit the diagnostic for bare traits. (We used to cancel for slightly better
|
||||
// error messages, but cancelling stashed diagnostics is no longer allowed because
|
||||
// it causes problems when tracking whether errors have actually occurred.)
|
||||
if span.edition().at_least_rust_2021() {
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
qself.span,
|
||||
StashKey::TraitMissingMethod,
|
||||
|_err| {},
|
||||
);
|
||||
}
|
||||
|
||||
if item_name.name != kw::Empty {
|
||||
|
@ -1941,53 +1941,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
errors_causecode: Vec<(Span, ObligationCauseCode<'tcx>)>,
|
||||
) {
|
||||
for (span, code) in errors_causecode {
|
||||
let Some(mut diag) = self.dcx().steal_diagnostic(span, StashKey::MaybeForgetReturn)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(fn_sig) = self.body_fn_sig()
|
||||
&& let ExprBindingObligation(_, _, hir_id, ..) = code
|
||||
&& !fn_sig.output().is_unit()
|
||||
{
|
||||
let mut block_num = 0;
|
||||
let mut found_semi = false;
|
||||
for (_, node) in self.tcx.hir().parent_iter(hir_id) {
|
||||
match node {
|
||||
hir::Node::Stmt(stmt) => {
|
||||
if let hir::StmtKind::Semi(expr) = stmt.kind {
|
||||
let expr_ty = self.typeck_results.borrow().expr_ty(expr);
|
||||
let return_ty = fn_sig.output();
|
||||
if !matches!(expr.kind, hir::ExprKind::Ret(..))
|
||||
&& self.can_coerce(expr_ty, return_ty)
|
||||
{
|
||||
found_semi = true;
|
||||
self.dcx().try_steal_modify_and_emit_err(span, StashKey::MaybeForgetReturn, |err| {
|
||||
if let Some(fn_sig) = self.body_fn_sig()
|
||||
&& let ExprBindingObligation(_, _, hir_id, ..) = code
|
||||
&& !fn_sig.output().is_unit()
|
||||
{
|
||||
let mut block_num = 0;
|
||||
let mut found_semi = false;
|
||||
for (_, node) in self.tcx.hir().parent_iter(hir_id) {
|
||||
match node {
|
||||
hir::Node::Stmt(stmt) => {
|
||||
if let hir::StmtKind::Semi(expr) = stmt.kind {
|
||||
let expr_ty = self.typeck_results.borrow().expr_ty(expr);
|
||||
let return_ty = fn_sig.output();
|
||||
if !matches!(expr.kind, hir::ExprKind::Ret(..))
|
||||
&& self.can_coerce(expr_ty, return_ty)
|
||||
{
|
||||
found_semi = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::Node::Block(_block) => {
|
||||
if found_semi {
|
||||
block_num += 1;
|
||||
hir::Node::Block(_block) => {
|
||||
if found_semi {
|
||||
block_num += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::Node::Item(item) => {
|
||||
if let hir::ItemKind::Fn(..) = item.kind {
|
||||
break;
|
||||
hir::Node::Item(item) => {
|
||||
if let hir::ItemKind::Fn(..) = item.kind {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if block_num > 1 && found_semi {
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
"you might have meant to return this to infer its type parameters",
|
||||
"return ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
if block_num > 1 && found_semi {
|
||||
diag.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
"you might have meant to return this to infer its type parameters",
|
||||
"return ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
diag.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2194,59 +2194,59 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let [seg1, seg2] = segs else {
|
||||
return;
|
||||
};
|
||||
let Some(mut diag) =
|
||||
self.dcx().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let map = self.infcx.tcx.hir();
|
||||
let body_id = self.tcx.hir().body_owned_by(self.body_id);
|
||||
let body = map.body(body_id);
|
||||
struct LetVisitor<'a> {
|
||||
result: Option<&'a hir::Expr<'a>>,
|
||||
ident_name: Symbol,
|
||||
}
|
||||
|
||||
// FIXME: This really should be taking scoping, etc into account.
|
||||
impl<'v> Visitor<'v> for LetVisitor<'v> {
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
||||
if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind
|
||||
&& let Binding(_, _, ident, ..) = pat.kind
|
||||
&& ident.name == self.ident_name
|
||||
{
|
||||
self.result = *init;
|
||||
} else {
|
||||
hir::intravisit::walk_stmt(self, ex);
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
seg1.ident.span,
|
||||
StashKey::CallAssocMethod,
|
||||
|err| {
|
||||
let map = self.infcx.tcx.hir();
|
||||
let body_id = self.tcx.hir().body_owned_by(self.body_id);
|
||||
let body = map.body(body_id);
|
||||
struct LetVisitor<'a> {
|
||||
result: Option<&'a hir::Expr<'a>>,
|
||||
ident_name: Symbol,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
|
||||
visitor.visit_body(body);
|
||||
// FIXME: This really should be taking scoping, etc into account.
|
||||
impl<'v> Visitor<'v> for LetVisitor<'v> {
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
||||
if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind
|
||||
&& let Binding(_, _, ident, ..) = pat.kind
|
||||
&& ident.name == self.ident_name
|
||||
{
|
||||
self.result = *init;
|
||||
} else {
|
||||
hir::intravisit::walk_stmt(self, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id)
|
||||
&& let Some(expr) = visitor.result
|
||||
&& let Some(self_ty) = self.node_ty_opt(expr.hir_id)
|
||||
{
|
||||
let probe = self.lookup_probe_for_diagnostic(
|
||||
seg2.ident,
|
||||
self_ty,
|
||||
call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
None,
|
||||
);
|
||||
if probe.is_ok() {
|
||||
let sm = self.infcx.tcx.sess.source_map();
|
||||
diag.span_suggestion_verbose(
|
||||
sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(),
|
||||
"you may have meant to call an instance method",
|
||||
".",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
diag.emit();
|
||||
let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
|
||||
visitor.visit_body(body);
|
||||
|
||||
if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id)
|
||||
&& let Some(expr) = visitor.result
|
||||
&& let Some(self_ty) = self.node_ty_opt(expr.hir_id)
|
||||
{
|
||||
let probe = self.lookup_probe_for_diagnostic(
|
||||
seg2.ident,
|
||||
self_ty,
|
||||
call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
None,
|
||||
);
|
||||
if probe.is_ok() {
|
||||
let sm = self.infcx.tcx.sess.source_map();
|
||||
err.span_suggestion_verbose(
|
||||
sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':')
|
||||
.unwrap(),
|
||||
"you may have meant to call an instance method",
|
||||
".",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()`
|
||||
|
@ -772,10 +772,6 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
|
||||
fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> ErrorGuaranteed {
|
||||
if let Some(guar) = self.fcx.dcx().has_errors() {
|
||||
guar
|
||||
} else if self.fcx.dcx().stashed_err_count() > 0 {
|
||||
// Without this case we sometimes get uninteresting and extraneous
|
||||
// "type annotations needed" errors.
|
||||
self.fcx.dcx().delayed_bug("error in Resolver")
|
||||
} else {
|
||||
self.fcx
|
||||
.err_ctxt()
|
||||
|
@ -87,7 +87,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
reported_signature_mismatch: self.reported_signature_mismatch.clone(),
|
||||
tainted_by_errors: self.tainted_by_errors.clone(),
|
||||
err_count_on_creation: self.err_count_on_creation,
|
||||
stashed_err_count_on_creation: self.stashed_err_count_on_creation,
|
||||
universe: self.universe.clone(),
|
||||
intercrate,
|
||||
next_trait_solver: self.next_trait_solver,
|
||||
|
@ -306,12 +306,6 @@ pub struct InferCtxt<'tcx> {
|
||||
// FIXME(matthewjasper) Merge into `tainted_by_errors`
|
||||
err_count_on_creation: usize,
|
||||
|
||||
/// Track how many errors were stashed when this infcx is created.
|
||||
/// Used for the same purpose as `err_count_on_creation`, even
|
||||
/// though it's weaker because the count can go up and down.
|
||||
// FIXME(matthewjasper) Merge into `tainted_by_errors`
|
||||
stashed_err_count_on_creation: usize,
|
||||
|
||||
/// What is the innermost universe we have created? Starts out as
|
||||
/// `UniverseIndex::root()` but grows from there as we enter
|
||||
/// universal quantifiers.
|
||||
@ -717,7 +711,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||
reported_signature_mismatch: Default::default(),
|
||||
tainted_by_errors: Cell::new(None),
|
||||
err_count_on_creation: tcx.dcx().err_count_excluding_lint_errs(),
|
||||
stashed_err_count_on_creation: tcx.dcx().stashed_err_count(),
|
||||
universe: Cell::new(ty::UniverseIndex::ROOT),
|
||||
intercrate,
|
||||
next_trait_solver,
|
||||
@ -1274,14 +1267,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
let guar = self.dcx().has_errors().unwrap();
|
||||
self.set_tainted_by_errors(guar);
|
||||
Some(guar)
|
||||
} else if self.dcx().stashed_err_count() > self.stashed_err_count_on_creation {
|
||||
// Errors stashed since this infcx was made. Not entirely reliable
|
||||
// because the count of stashed errors can go down. But without
|
||||
// this case we get a moderate number of uninteresting and
|
||||
// extraneous "type annotations needed" errors.
|
||||
let guar = self.dcx().delayed_bug("tainted_by_errors: stashed bug awaiting emission");
|
||||
self.set_tainted_by_errors(guar);
|
||||
Some(guar)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -70,15 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
|
||||
|
||||
// Check if the method call actually calls the libcore
|
||||
// `IntoIterator::into_iter`.
|
||||
let trait_id = cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.and_then(|did| cx.tcx.trait_of_item(did));
|
||||
if trait_id.is_none()
|
||||
|| !cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id.unwrap())
|
||||
{
|
||||
return;
|
||||
}
|
||||
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
|
||||
match cx.tcx.trait_of_item(def_id) {
|
||||
Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// As this is a method call expression, we have at least one argument.
|
||||
let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
|
||||
|
@ -815,7 +815,9 @@ pub enum PatKind<'tcx> {
|
||||
/// The boundaries must be of the same type and that type must be numeric.
|
||||
#[derive(Clone, Debug, PartialEq, HashStable, TypeVisitable)]
|
||||
pub struct PatRange<'tcx> {
|
||||
/// Must not be `PosInfinity`.
|
||||
pub lo: PatRangeBoundary<'tcx>,
|
||||
/// Must not be `NegInfinity`.
|
||||
pub hi: PatRangeBoundary<'tcx>,
|
||||
#[type_visitable(ignore)]
|
||||
pub end: RangeEnd,
|
||||
@ -958,22 +960,6 @@ impl<'tcx> PatRangeBoundary<'tcx> {
|
||||
Self::NegInfinity | Self::PosInfinity => None,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn to_const(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
|
||||
match self {
|
||||
Self::Finite(value) => value,
|
||||
Self::NegInfinity => {
|
||||
// Unwrap is ok because the type is known to be numeric.
|
||||
let c = ty.numeric_min_val(tcx).unwrap();
|
||||
mir::Const::from_ty_const(c, tcx)
|
||||
}
|
||||
Self::PosInfinity => {
|
||||
// Unwrap is ok because the type is known to be numeric.
|
||||
let c = ty.numeric_max_val(tcx).unwrap();
|
||||
mir::Const::from_ty_const(c, tcx)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u128 {
|
||||
match self {
|
||||
Self::Finite(value) => value.eval_bits(tcx, param_env),
|
||||
|
@ -847,13 +847,15 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
|
||||
opaque_def_id: LocalDefId,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Result<Diag<'tcx>, ErrorGuaranteed> {
|
||||
if let Some(diag) = tcx
|
||||
.sess
|
||||
.dcx()
|
||||
.steal_diagnostic(tcx.def_span(opaque_def_id), StashKey::OpaqueHiddenTypeMismatch)
|
||||
{
|
||||
diag.cancel();
|
||||
}
|
||||
// We used to cancel here for slightly better error messages, but
|
||||
// cancelling stashed diagnostics is no longer allowed because it
|
||||
// causes problems when tracking whether errors have actually
|
||||
// occurred.
|
||||
tcx.sess.dcx().try_steal_modify_and_emit_err(
|
||||
tcx.def_span(opaque_def_id),
|
||||
StashKey::OpaqueHiddenTypeMismatch,
|
||||
|_err| {},
|
||||
);
|
||||
(self.ty, other.ty).error_reported()?;
|
||||
// Found different concrete types for the opaque type.
|
||||
let sub_diag = if self.span == other.span {
|
||||
|
@ -91,8 +91,8 @@ impl<'tcx> Region<'tcx> {
|
||||
|
||||
/// Constructs a `RegionKind::ReError` region.
|
||||
#[track_caller]
|
||||
pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Region<'tcx> {
|
||||
tcx.intern_region(ty::ReError(reported))
|
||||
pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Region<'tcx> {
|
||||
tcx.intern_region(ty::ReError(guar))
|
||||
}
|
||||
|
||||
/// Constructs a `RegionKind::ReError` region and registers a delayed bug to ensure it gets
|
||||
|
@ -1528,8 +1528,8 @@ impl<'tcx> Ty<'tcx> {
|
||||
}
|
||||
|
||||
/// Constructs a `TyKind::Error` type with current `ErrorGuaranteed`
|
||||
pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Ty<'tcx> {
|
||||
Ty::new(tcx, Error(reported))
|
||||
pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Ty<'tcx> {
|
||||
Ty::new(tcx, Error(guar))
|
||||
}
|
||||
|
||||
/// Constructs a `TyKind::Error` type and registers a `span_delayed_bug` to ensure it gets used.
|
||||
|
@ -291,33 +291,41 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
TestKind::Range(ref range) => {
|
||||
let lower_bound_success = self.cfg.start_new_block();
|
||||
|
||||
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
|
||||
// FIXME: skip useless comparison when the range is half-open.
|
||||
let lo = range.lo.to_const(range.ty, self.tcx);
|
||||
let hi = range.hi.to_const(range.ty, self.tcx);
|
||||
let lo = self.literal_operand(test.span, lo);
|
||||
let hi = self.literal_operand(test.span, hi);
|
||||
let val = Operand::Copy(place);
|
||||
|
||||
let [success, fail] = *target_blocks else {
|
||||
bug!("`TestKind::Range` should have two target blocks");
|
||||
};
|
||||
self.compare(
|
||||
block,
|
||||
lower_bound_success,
|
||||
fail,
|
||||
source_info,
|
||||
BinOp::Le,
|
||||
lo,
|
||||
val.clone(),
|
||||
);
|
||||
let op = match range.end {
|
||||
RangeEnd::Included => BinOp::Le,
|
||||
RangeEnd::Excluded => BinOp::Lt,
|
||||
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
|
||||
let val = Operand::Copy(place);
|
||||
|
||||
let intermediate_block = if !range.lo.is_finite() {
|
||||
block
|
||||
} else if !range.hi.is_finite() {
|
||||
success
|
||||
} else {
|
||||
self.cfg.start_new_block()
|
||||
};
|
||||
self.compare(lower_bound_success, success, fail, source_info, op, val, hi);
|
||||
|
||||
if let Some(lo) = range.lo.as_finite() {
|
||||
let lo = self.literal_operand(test.span, lo);
|
||||
self.compare(
|
||||
block,
|
||||
intermediate_block,
|
||||
fail,
|
||||
source_info,
|
||||
BinOp::Le,
|
||||
lo,
|
||||
val.clone(),
|
||||
);
|
||||
};
|
||||
|
||||
if let Some(hi) = range.hi.as_finite() {
|
||||
let hi = self.literal_operand(test.span, hi);
|
||||
let op = match range.end {
|
||||
RangeEnd::Included => BinOp::Le,
|
||||
RangeEnd::Excluded => BinOp::Lt,
|
||||
};
|
||||
self.compare(intermediate_block, success, fail, source_info, op, val, hi);
|
||||
}
|
||||
}
|
||||
|
||||
TestKind::Len { len, op } => {
|
||||
|
@ -1762,24 +1762,25 @@ impl<'a> Parser<'a> {
|
||||
err: impl FnOnce(&Self) -> Diag<'a>,
|
||||
) -> L {
|
||||
assert!(could_be_unclosed_char_literal(ident));
|
||||
if let Some(diag) = self.dcx().steal_diagnostic(ident.span, StashKey::LifetimeIsChar) {
|
||||
diag.with_span_suggestion_verbose(
|
||||
ident.span.shrink_to_hi(),
|
||||
"add `'` to close the char literal",
|
||||
"'",
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit();
|
||||
} else {
|
||||
err(self)
|
||||
.with_span_suggestion_verbose(
|
||||
self.dcx()
|
||||
.try_steal_modify_and_emit_err(ident.span, StashKey::LifetimeIsChar, |err| {
|
||||
err.span_suggestion_verbose(
|
||||
ident.span.shrink_to_hi(),
|
||||
"add `'` to close the char literal",
|
||||
"'",
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
err(self)
|
||||
.with_span_suggestion_verbose(
|
||||
ident.span.shrink_to_hi(),
|
||||
"add `'` to close the char literal",
|
||||
"'",
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit()
|
||||
});
|
||||
let name = ident.without_first_quote().name;
|
||||
mk_lit_char(name, ident.span)
|
||||
}
|
||||
|
@ -2520,14 +2520,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
||||
if attr.style == AttrStyle::Inner {
|
||||
for attr_to_check in ATTRS_TO_CHECK {
|
||||
if attr.has_name(*attr_to_check) {
|
||||
if let AttrKind::Normal(ref p) = attr.kind
|
||||
&& let Some(diag) = tcx.dcx().steal_diagnostic(
|
||||
p.item.path.span,
|
||||
StashKey::UndeterminedMacroResolution,
|
||||
)
|
||||
{
|
||||
diag.cancel();
|
||||
}
|
||||
let item = tcx
|
||||
.hir()
|
||||
.items()
|
||||
@ -2537,7 +2529,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
||||
span: item.ident.span,
|
||||
kind: item.kind.descr(),
|
||||
});
|
||||
tcx.dcx().emit_err(errors::InvalidAttrAtCrateLevel {
|
||||
let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel {
|
||||
span: attr.span,
|
||||
sugg_span: tcx
|
||||
.sess
|
||||
@ -2553,6 +2545,16 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
||||
name: *attr_to_check,
|
||||
item,
|
||||
});
|
||||
|
||||
if let AttrKind::Normal(ref p) = attr.kind {
|
||||
tcx.dcx().try_steal_replace_and_emit_err(
|
||||
p.item.path.span,
|
||||
StashKey::UndeterminedMacroResolution,
|
||||
err,
|
||||
);
|
||||
} else {
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -688,6 +688,9 @@ pub enum Constructor<Cx: TypeCx> {
|
||||
/// Fake extra constructor for constructors that are not seen in the matrix, as explained at the
|
||||
/// top of the file.
|
||||
Missing,
|
||||
/// Fake extra constructor that indicates and empty field that is private. When we encounter one
|
||||
/// we skip the column entirely so we don't observe its emptiness. Only used for specialization.
|
||||
PrivateUninhabited,
|
||||
}
|
||||
|
||||
impl<Cx: TypeCx> Clone for Constructor<Cx> {
|
||||
@ -709,6 +712,7 @@ impl<Cx: TypeCx> Clone for Constructor<Cx> {
|
||||
Constructor::NonExhaustive => Constructor::NonExhaustive,
|
||||
Constructor::Hidden => Constructor::Hidden,
|
||||
Constructor::Missing => Constructor::Missing,
|
||||
Constructor::PrivateUninhabited => Constructor::PrivateUninhabited,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -763,6 +767,8 @@ impl<Cx: TypeCx> Constructor<Cx> {
|
||||
}
|
||||
// Wildcards cover anything
|
||||
(_, Wildcard) => true,
|
||||
// `PrivateUninhabited` skips everything.
|
||||
(PrivateUninhabited, _) => true,
|
||||
// Only a wildcard pattern can match these special constructors.
|
||||
(Missing { .. } | NonExhaustive | Hidden, _) => false,
|
||||
|
||||
|
@ -82,6 +82,11 @@ use crate::usefulness::{compute_match_usefulness, ValidityConstraint};
|
||||
pub trait Captures<'a> {}
|
||||
impl<'a, T: ?Sized> Captures<'a> for T {}
|
||||
|
||||
/// `bool` newtype that indicates whether this is a privately uninhabited field that we should skip
|
||||
/// during analysis.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PrivateUninhabitedField(pub bool);
|
||||
|
||||
/// Context that provides type information about constructors.
|
||||
///
|
||||
/// Most of the crate is parameterized on a type that implements this trait.
|
||||
@ -105,13 +110,12 @@ pub trait TypeCx: Sized + fmt::Debug {
|
||||
/// The number of fields for this constructor.
|
||||
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize;
|
||||
|
||||
/// The types of the fields for this constructor. The result must have a length of
|
||||
/// `ctor_arity()`.
|
||||
/// The types of the fields for this constructor. The result must contain `ctor_arity()` fields.
|
||||
fn ctor_sub_tys<'a>(
|
||||
&'a self,
|
||||
ctor: &'a Constructor<Self>,
|
||||
ty: &'a Self::Ty,
|
||||
) -> impl Iterator<Item = Self::Ty> + ExactSizeIterator + Captures<'a>;
|
||||
) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator + Captures<'a>;
|
||||
|
||||
/// The set of all the constructors for `ty`.
|
||||
///
|
||||
|
@ -5,7 +5,7 @@ use std::fmt;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::constructor::{Constructor, Slice, SliceKind};
|
||||
use crate::TypeCx;
|
||||
use crate::{PrivateUninhabitedField, TypeCx};
|
||||
|
||||
use self::Constructor::*;
|
||||
|
||||
@ -23,11 +23,6 @@ impl PatId {
|
||||
/// Values and patterns can be represented as a constructor applied to some fields. This represents
|
||||
/// a pattern in this form. A `DeconstructedPat` will almost always come from user input; the only
|
||||
/// exception are some `Wildcard`s introduced during pattern lowering.
|
||||
///
|
||||
/// Note that the number of fields may not match the fields declared in the original struct/variant.
|
||||
/// This happens if a private or `non_exhaustive` field is uninhabited, because the code mustn't
|
||||
/// observe that it is uninhabited. In that case that field is not included in `fields`. Care must
|
||||
/// be taken when converting to/from `thir::Pat`.
|
||||
pub struct DeconstructedPat<Cx: TypeCx> {
|
||||
ctor: Constructor<Cx>,
|
||||
fields: Vec<DeconstructedPat<Cx>>,
|
||||
@ -84,6 +79,8 @@ impl<Cx: TypeCx> DeconstructedPat<Cx> {
|
||||
match (&self.ctor, other_ctor) {
|
||||
// Return a wildcard for each field of `other_ctor`.
|
||||
(Wildcard, _) => wildcard_sub_tys(),
|
||||
// Skip this column.
|
||||
(_, PrivateUninhabited) => smallvec![],
|
||||
// The only non-trivial case: two slices of different arity. `other_slice` is
|
||||
// guaranteed to have a larger arity, so we fill the middle part with enough
|
||||
// wildcards to reach the length of the new, larger slice.
|
||||
@ -192,7 +189,9 @@ impl<Cx: TypeCx> fmt::Debug for DeconstructedPat<Cx> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Wildcard | Missing { .. } | NonExhaustive | Hidden => write!(f, "_ : {:?}", pat.ty()),
|
||||
Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => {
|
||||
write!(f, "_ : {:?}", pat.ty())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -300,7 +299,11 @@ impl<Cx: TypeCx> WitnessPat<Cx> {
|
||||
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
|
||||
/// `Some(_)`.
|
||||
pub(crate) fn wild_from_ctor(cx: &Cx, ctor: Constructor<Cx>, ty: Cx::Ty) -> Self {
|
||||
let fields = cx.ctor_sub_tys(&ctor, &ty).map(|ty| Self::wildcard(ty)).collect();
|
||||
let fields = cx
|
||||
.ctor_sub_tys(&ctor, &ty)
|
||||
.filter(|(_, PrivateUninhabitedField(skip))| !skip)
|
||||
.map(|(ty, _)| Self::wildcard(ty))
|
||||
.collect();
|
||||
Self::new(ctor, fields, ty)
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::mir::{self, Const};
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef};
|
||||
use rustc_middle::ty::{self, FieldDef, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
|
||||
use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
|
||||
@ -18,7 +18,7 @@ use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
|
||||
use crate::constructor::{
|
||||
IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility,
|
||||
};
|
||||
use crate::{errors, Captures, TypeCx};
|
||||
use crate::{errors, Captures, PrivateUninhabitedField, TypeCx};
|
||||
|
||||
use crate::constructor::Constructor::*;
|
||||
|
||||
@ -158,34 +158,19 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide
|
||||
// uninhabited fields in order not to reveal the uninhabitedness of the whole variant.
|
||||
// This lists the fields we keep along with their types.
|
||||
pub(crate) fn list_variant_nonhidden_fields(
|
||||
pub(crate) fn variant_sub_tys(
|
||||
&self,
|
||||
ty: RevealedTy<'tcx>,
|
||||
variant: &'tcx VariantDef,
|
||||
) -> impl Iterator<Item = (FieldIdx, RevealedTy<'tcx>)> + Captures<'p> + Captures<'_> {
|
||||
let cx = self;
|
||||
let ty::Adt(adt, args) = ty.kind() else { bug!() };
|
||||
// Whether we must not match the fields of this variant exhaustively.
|
||||
let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did().is_local();
|
||||
|
||||
variant.fields.iter().enumerate().filter_map(move |(i, field)| {
|
||||
let ty = field.ty(cx.tcx, args);
|
||||
) -> impl Iterator<Item = (&'tcx FieldDef, RevealedTy<'tcx>)> + Captures<'p> + Captures<'_>
|
||||
{
|
||||
let ty::Adt(_, args) = ty.kind() else { bug!() };
|
||||
variant.fields.iter().map(move |field| {
|
||||
let ty = field.ty(self.tcx, args);
|
||||
// `field.ty()` doesn't normalize after instantiating.
|
||||
let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
||||
let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
|
||||
let is_uninhabited = (cx.tcx.features().exhaustive_patterns
|
||||
|| cx.tcx.features().min_exhaustive_patterns)
|
||||
&& cx.is_uninhabited(ty);
|
||||
|
||||
if is_uninhabited && (!is_visible || is_non_exhaustive) {
|
||||
None
|
||||
} else {
|
||||
let ty = cx.reveal_opaque_ty(ty);
|
||||
Some((FieldIdx::new(i), ty))
|
||||
}
|
||||
let ty = self.tcx.normalize_erasing_regions(self.param_env, ty);
|
||||
let ty = self.reveal_opaque_ty(ty);
|
||||
(field, ty)
|
||||
})
|
||||
}
|
||||
|
||||
@ -210,12 +195,17 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
&'a self,
|
||||
ctor: &'a Constructor<'p, 'tcx>,
|
||||
ty: RevealedTy<'tcx>,
|
||||
) -> impl Iterator<Item = RevealedTy<'tcx>> + ExactSizeIterator + Captures<'a> {
|
||||
) -> impl Iterator<Item = (RevealedTy<'tcx>, PrivateUninhabitedField)>
|
||||
+ ExactSizeIterator
|
||||
+ Captures<'a> {
|
||||
fn reveal_and_alloc<'a, 'tcx>(
|
||||
cx: &'a RustcMatchCheckCtxt<'_, 'tcx>,
|
||||
iter: impl Iterator<Item = Ty<'tcx>>,
|
||||
) -> &'a [RevealedTy<'tcx>] {
|
||||
cx.dropless_arena.alloc_from_iter(iter.map(|ty| cx.reveal_opaque_ty(ty)))
|
||||
) -> &'a [(RevealedTy<'tcx>, PrivateUninhabitedField)] {
|
||||
cx.dropless_arena.alloc_from_iter(
|
||||
iter.map(|ty| cx.reveal_opaque_ty(ty))
|
||||
.map(|ty| (ty, PrivateUninhabitedField(false))),
|
||||
)
|
||||
}
|
||||
let cx = self;
|
||||
let slice = match ctor {
|
||||
@ -229,7 +219,21 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
} else {
|
||||
let variant =
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
let tys = cx.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty);
|
||||
|
||||
// In the cases of either a `#[non_exhaustive]` field list or a non-public
|
||||
// field, we skip uninhabited fields in order not to reveal the
|
||||
// uninhabitedness of the whole variant.
|
||||
let is_non_exhaustive =
|
||||
variant.is_field_list_non_exhaustive() && !adt.did().is_local();
|
||||
let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
|
||||
let is_visible =
|
||||
adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
|
||||
let is_uninhabited = (cx.tcx.features().exhaustive_patterns
|
||||
|| cx.tcx.features().min_exhaustive_patterns)
|
||||
&& cx.is_uninhabited(*ty);
|
||||
let skip = is_uninhabited && (!is_visible || is_non_exhaustive);
|
||||
(ty, PrivateUninhabitedField(skip))
|
||||
});
|
||||
cx.dropless_arena.alloc_from_iter(tys)
|
||||
}
|
||||
}
|
||||
@ -246,16 +250,8 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
|
||||
},
|
||||
Bool(..)
|
||||
| IntRange(..)
|
||||
| F32Range(..)
|
||||
| F64Range(..)
|
||||
| Str(..)
|
||||
| Opaque(..)
|
||||
| NonExhaustive
|
||||
| Hidden
|
||||
| Missing { .. }
|
||||
| Wildcard => &[],
|
||||
Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
|
||||
| NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[],
|
||||
Or => {
|
||||
bug!("called `Fields::wildcards` on an `Or` ctor")
|
||||
}
|
||||
@ -274,25 +270,16 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
// patterns. If we're here we can assume this is a box pattern.
|
||||
1
|
||||
} else {
|
||||
let variant =
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
self.list_variant_nonhidden_fields(ty, variant).count()
|
||||
let variant_idx = RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt);
|
||||
adt.variant(variant_idx).fields.len()
|
||||
}
|
||||
}
|
||||
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
|
||||
},
|
||||
Ref => 1,
|
||||
Slice(slice) => slice.arity(),
|
||||
Bool(..)
|
||||
| IntRange(..)
|
||||
| F32Range(..)
|
||||
| F64Range(..)
|
||||
| Str(..)
|
||||
| Opaque(..)
|
||||
| NonExhaustive
|
||||
| Hidden
|
||||
| Missing { .. }
|
||||
| Wildcard => 0,
|
||||
Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
|
||||
| NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0,
|
||||
Or => bug!("The `Or` constructor doesn't have a fixed arity"),
|
||||
}
|
||||
}
|
||||
@ -520,20 +507,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
};
|
||||
let variant =
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
// For each field in the variant, we store the relevant index into `self.fields` if any.
|
||||
let mut field_id_to_id: Vec<Option<usize>> =
|
||||
(0..variant.fields.len()).map(|_| None).collect();
|
||||
let tys = cx.list_variant_nonhidden_fields(ty, variant).enumerate().map(
|
||||
|(i, (field, ty))| {
|
||||
field_id_to_id[field.index()] = Some(i);
|
||||
ty
|
||||
},
|
||||
);
|
||||
fields = tys.map(|ty| DeconstructedPat::wildcard(ty)).collect();
|
||||
fields = cx
|
||||
.variant_sub_tys(ty, variant)
|
||||
.map(|(_, ty)| DeconstructedPat::wildcard(ty))
|
||||
.collect();
|
||||
for pat in subpatterns {
|
||||
if let Some(i) = field_id_to_id[pat.field.index()] {
|
||||
fields[i] = self.lower_pat(&pat.pattern);
|
||||
}
|
||||
fields[pat.field.index()] = self.lower_pat(&pat.pattern);
|
||||
}
|
||||
}
|
||||
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
|
||||
@ -775,11 +754,9 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
ty::Adt(adt_def, args) => {
|
||||
let variant_index =
|
||||
RustcMatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def);
|
||||
let variant = &adt_def.variant(variant_index);
|
||||
let subpatterns = cx
|
||||
.list_variant_nonhidden_fields(*pat.ty(), variant)
|
||||
.zip(subpatterns)
|
||||
.map(|((field, _ty), pattern)| FieldPat { field, pattern })
|
||||
let subpatterns = subpatterns
|
||||
.enumerate()
|
||||
.map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
|
||||
.collect();
|
||||
|
||||
if adt_def.is_enum() {
|
||||
@ -830,7 +807,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
&Str(value) => PatKind::Constant { value },
|
||||
Wildcard | NonExhaustive | Hidden => PatKind::Wild,
|
||||
Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
|
||||
Missing { .. } => bug!(
|
||||
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
|
||||
`Missing` should have been processed in `apply_constructors`"
|
||||
@ -866,7 +843,8 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
&'a self,
|
||||
ctor: &'a crate::constructor::Constructor<Self>,
|
||||
ty: &'a Self::Ty,
|
||||
) -> impl Iterator<Item = Self::Ty> + ExactSizeIterator + Captures<'a> {
|
||||
) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator + Captures<'a>
|
||||
{
|
||||
self.ctor_sub_tys(ctor, *ty)
|
||||
}
|
||||
fn ctors_for_ty(
|
||||
|
@ -716,7 +716,7 @@ use std::fmt;
|
||||
|
||||
use crate::constructor::{Constructor, ConstructorSet, IntRange};
|
||||
use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat};
|
||||
use crate::{Captures, MatchArm, TypeCx};
|
||||
use crate::{Captures, MatchArm, PrivateUninhabitedField, TypeCx};
|
||||
|
||||
use self::ValidityConstraint::*;
|
||||
|
||||
@ -817,6 +817,9 @@ impl fmt::Display for ValidityConstraint {
|
||||
struct PlaceInfo<Cx: TypeCx> {
|
||||
/// The type of the place.
|
||||
ty: Cx::Ty,
|
||||
/// Whether the place is a private uninhabited field. If so we skip this field during analysis
|
||||
/// so that we don't observe its emptiness.
|
||||
private_uninhabited: bool,
|
||||
/// Whether the place is known to contain valid data.
|
||||
validity: ValidityConstraint,
|
||||
/// Whether the place is the scrutinee itself or a subplace of it.
|
||||
@ -833,8 +836,9 @@ impl<Cx: TypeCx> PlaceInfo<Cx> {
|
||||
) -> impl Iterator<Item = Self> + ExactSizeIterator + Captures<'a> {
|
||||
let ctor_sub_tys = cx.ctor_sub_tys(ctor, &self.ty);
|
||||
let ctor_sub_validity = self.validity.specialize(ctor);
|
||||
ctor_sub_tys.map(move |ty| PlaceInfo {
|
||||
ctor_sub_tys.map(move |(ty, PrivateUninhabitedField(private_uninhabited))| PlaceInfo {
|
||||
ty,
|
||||
private_uninhabited,
|
||||
validity: ctor_sub_validity,
|
||||
is_scrutinee: false,
|
||||
})
|
||||
@ -856,6 +860,11 @@ impl<Cx: TypeCx> PlaceInfo<Cx> {
|
||||
where
|
||||
Cx: 'a,
|
||||
{
|
||||
if self.private_uninhabited {
|
||||
// Skip the whole column
|
||||
return Ok((smallvec![Constructor::PrivateUninhabited], vec![]));
|
||||
}
|
||||
|
||||
let ctors_for_ty = cx.ctors_for_ty(&self.ty)?;
|
||||
|
||||
// We treat match scrutinees of type `!` or `EmptyEnum` differently.
|
||||
@ -914,7 +923,12 @@ impl<Cx: TypeCx> PlaceInfo<Cx> {
|
||||
|
||||
impl<Cx: TypeCx> Clone for PlaceInfo<Cx> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { ty: self.ty.clone(), validity: self.validity, is_scrutinee: self.is_scrutinee }
|
||||
Self {
|
||||
ty: self.ty.clone(),
|
||||
private_uninhabited: self.private_uninhabited,
|
||||
validity: self.validity,
|
||||
is_scrutinee: self.is_scrutinee,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1121,7 +1135,12 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
|
||||
scrut_ty: Cx::Ty,
|
||||
scrut_validity: ValidityConstraint,
|
||||
) -> Self {
|
||||
let place_info = PlaceInfo { ty: scrut_ty, validity: scrut_validity, is_scrutinee: true };
|
||||
let place_info = PlaceInfo {
|
||||
ty: scrut_ty,
|
||||
private_uninhabited: false,
|
||||
validity: scrut_validity,
|
||||
is_scrutinee: true,
|
||||
};
|
||||
let mut matrix = Matrix {
|
||||
rows: Vec::with_capacity(arms.len()),
|
||||
place_info: smallvec![place_info],
|
||||
|
@ -988,10 +988,7 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
|
||||
if let hir::ExprKind::Struct(qpath, fields, ref base) = expr.kind {
|
||||
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
|
||||
let Some(adt) = self.typeck_results().expr_ty(expr).ty_adt_def() else {
|
||||
self.tcx.dcx().span_delayed_bug(expr.span, "no adt_def for expression");
|
||||
return;
|
||||
};
|
||||
let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap();
|
||||
let variant = adt.variant_of_res(res);
|
||||
if let Some(base) = *base {
|
||||
// If the expression uses FRU we need to make sure all the unmentioned fields
|
||||
|
@ -149,8 +149,7 @@ where
|
||||
let guar = if let Some(root) = cycle_error.cycle.first()
|
||||
&& let Some(span) = root.query.span
|
||||
{
|
||||
error.stash(span, StashKey::Cycle);
|
||||
qcx.dep_context().sess().dcx().span_delayed_bug(span, "delayed cycle error")
|
||||
error.stash(span, StashKey::Cycle).unwrap()
|
||||
} else {
|
||||
error.emit()
|
||||
};
|
||||
|
@ -13,9 +13,10 @@ use crate::Session;
|
||||
use rustc_ast::node_id::NodeId;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc};
|
||||
use rustc_errors::{emitter::SilentEmitter, DiagCtxt};
|
||||
use rustc_errors::emitter::{stderr_destination, HumanEmitter, SilentEmitter};
|
||||
use rustc_errors::{
|
||||
fallback_fluent_bundle, Diag, DiagnosticMessage, EmissionGuarantee, MultiSpan, StashKey,
|
||||
fallback_fluent_bundle, ColorConfig, Diag, DiagCtxt, DiagnosticMessage, EmissionGuarantee,
|
||||
MultiSpan, StashKey,
|
||||
};
|
||||
use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
|
||||
use rustc_span::edition::Edition;
|
||||
@ -105,8 +106,7 @@ pub fn feature_err_issue(
|
||||
|
||||
// Cancel an earlier warning for this same error, if it exists.
|
||||
if let Some(span) = span.primary_span() {
|
||||
if let Some(err) = sess.parse_sess.dcx.steal_diagnostic(span, StashKey::EarlySyntaxWarning)
|
||||
{
|
||||
if let Some(err) = sess.parse_sess.dcx.steal_non_err(span, StashKey::EarlySyntaxWarning) {
|
||||
err.cancel()
|
||||
}
|
||||
}
|
||||
@ -236,7 +236,11 @@ impl ParseSess {
|
||||
pub fn new(locale_resources: Vec<&'static str>, file_path_mapping: FilePathMapping) -> Self {
|
||||
let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
|
||||
let sm = Lrc::new(SourceMap::new(file_path_mapping));
|
||||
let dcx = DiagCtxt::with_tty_emitter(Some(sm.clone()), fallback_bundle);
|
||||
let emitter = Box::new(
|
||||
HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle)
|
||||
.sm(Some(sm.clone())),
|
||||
);
|
||||
let dcx = DiagCtxt::new(emitter);
|
||||
ParseSess::with_dcx(dcx, sm)
|
||||
}
|
||||
|
||||
@ -265,9 +269,11 @@ impl ParseSess {
|
||||
pub fn with_silent_emitter(fatal_note: String) -> Self {
|
||||
let fallback_bundle = fallback_fluent_bundle(Vec::new(), false);
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let fatal_dcx = DiagCtxt::with_tty_emitter(None, fallback_bundle).disable_warnings();
|
||||
let dcx = DiagCtxt::with_emitter(Box::new(SilentEmitter { fatal_dcx, fatal_note }))
|
||||
.disable_warnings();
|
||||
let emitter =
|
||||
Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle));
|
||||
let fatal_dcx = DiagCtxt::new(emitter);
|
||||
let dcx =
|
||||
DiagCtxt::new(Box::new(SilentEmitter { fatal_dcx, fatal_note })).disable_warnings();
|
||||
ParseSess::with_dcx(dcx, sm)
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ use rustc_data_structures::sync::{
|
||||
AtomicU64, DynSend, DynSync, Lock, Lrc, MappedReadGuard, ReadGuard, RwLock,
|
||||
};
|
||||
use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter;
|
||||
use rustc_errors::emitter::{DynEmitter, HumanEmitter, HumanReadableErrorType};
|
||||
use rustc_errors::emitter::{stderr_destination, DynEmitter, HumanEmitter, HumanReadableErrorType};
|
||||
use rustc_errors::json::JsonEmitter;
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::{
|
||||
@ -28,7 +28,7 @@ use rustc_errors::{
|
||||
use rustc_macros::HashStable_Generic;
|
||||
pub use rustc_span::def_id::StableCrateId;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMap};
|
||||
use rustc_span::source_map::{FileLoader, FilePathMapping, RealFileLoader, SourceMap};
|
||||
use rustc_span::{SourceFileHashAlgorithm, Span, Symbol};
|
||||
use rustc_target::asm::InlineAsmArch;
|
||||
use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel};
|
||||
@ -39,6 +39,7 @@ use rustc_target::spec::{
|
||||
use std::any::Any;
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::ops::{Div, Mul};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
@ -982,7 +983,7 @@ fn default_emitter(
|
||||
);
|
||||
Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
|
||||
} else {
|
||||
let emitter = HumanEmitter::stderr(color_config, fallback_bundle)
|
||||
let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
|
||||
.fluent_bundle(bundle)
|
||||
.sm(Some(source_map))
|
||||
.short_message(short)
|
||||
@ -998,22 +999,23 @@ fn default_emitter(
|
||||
}
|
||||
}
|
||||
config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(
|
||||
JsonEmitter::stderr(
|
||||
Some(registry),
|
||||
JsonEmitter::new(
|
||||
Box::new(io::BufWriter::new(io::stderr())),
|
||||
source_map,
|
||||
bundle,
|
||||
fallback_bundle,
|
||||
pretty,
|
||||
json_rendered,
|
||||
sopts.diagnostic_width,
|
||||
macro_backtrace,
|
||||
track_diagnostics,
|
||||
terminal_url,
|
||||
)
|
||||
.registry(Some(registry))
|
||||
.fluent_bundle(bundle)
|
||||
.ui_testing(sopts.unstable_opts.ui_testing)
|
||||
.ignored_directories_in_source_blocks(
|
||||
sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(),
|
||||
),
|
||||
)
|
||||
.diagnostic_width(sopts.diagnostic_width)
|
||||
.macro_backtrace(macro_backtrace)
|
||||
.track_diagnostics(track_diagnostics)
|
||||
.terminal_url(terminal_url),
|
||||
),
|
||||
}
|
||||
}
|
||||
@ -1080,8 +1082,8 @@ pub fn build_session(
|
||||
);
|
||||
let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle);
|
||||
|
||||
let mut dcx = DiagCtxt::with_emitter(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));
|
||||
if let Some(ice_file) = ice_file {
|
||||
dcx = dcx.with_ice_file(ice_file);
|
||||
}
|
||||
@ -1402,7 +1404,7 @@ pub struct EarlyDiagCtxt {
|
||||
impl EarlyDiagCtxt {
|
||||
pub fn new(output: ErrorOutputType) -> Self {
|
||||
let emitter = mk_emitter(output);
|
||||
Self { dcx: DiagCtxt::with_emitter(emitter) }
|
||||
Self { dcx: DiagCtxt::new(emitter) }
|
||||
}
|
||||
|
||||
/// Swap out the underlying dcx once we acquire the user's preference on error emission
|
||||
@ -1412,7 +1414,7 @@ impl EarlyDiagCtxt {
|
||||
self.dcx.abort_if_errors();
|
||||
|
||||
let emitter = mk_emitter(output);
|
||||
self.dcx = DiagCtxt::with_emitter(emitter);
|
||||
self.dcx = DiagCtxt::new(emitter);
|
||||
}
|
||||
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
@ -1473,17 +1475,17 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
|
||||
let emitter: Box<DynEmitter> = match output {
|
||||
config::ErrorOutputType::HumanReadable(kind) => {
|
||||
let (short, color_config) = kind.unzip();
|
||||
Box::new(HumanEmitter::stderr(color_config, fallback_bundle).short_message(short))
|
||||
Box::new(
|
||||
HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
|
||||
.short_message(short),
|
||||
)
|
||||
}
|
||||
config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::basic(
|
||||
config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::new(
|
||||
Box::new(io::BufWriter::new(io::stderr())),
|
||||
Lrc::new(SourceMap::new(FilePathMapping::empty())),
|
||||
fallback_bundle,
|
||||
pretty,
|
||||
json_rendered,
|
||||
None,
|
||||
fallback_bundle,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
TerminalUrl::No,
|
||||
)),
|
||||
};
|
||||
emitter
|
||||
|
@ -3,6 +3,7 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target};
|
||||
pub fn target() -> Target {
|
||||
let mut base = base::windows_gnu::opts();
|
||||
base.cpu = "x86-64".into();
|
||||
base.features = "+cx16,+sse3,+sahf".into();
|
||||
base.plt_by_default = false;
|
||||
// Use high-entropy 64 bit address space for ASLR
|
||||
base.add_pre_link_args(
|
||||
@ -10,7 +11,7 @@ pub fn target() -> Target {
|
||||
&["-m", "i386pep", "--high-entropy-va"],
|
||||
);
|
||||
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]);
|
||||
base.max_atomic_width = Some(64);
|
||||
base.max_atomic_width = Some(128);
|
||||
base.linker = Some("x86_64-w64-mingw32-gcc".into());
|
||||
|
||||
Target {
|
||||
|
@ -3,9 +3,10 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target};
|
||||
pub fn target() -> Target {
|
||||
let mut base = base::windows_gnullvm::opts();
|
||||
base.cpu = "x86-64".into();
|
||||
base.features = "+cx16,+sse3,+sahf".into();
|
||||
base.plt_by_default = false;
|
||||
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
|
||||
base.max_atomic_width = Some(64);
|
||||
base.max_atomic_width = Some(128);
|
||||
base.linker = Some("x86_64-w64-mingw32-clang".into());
|
||||
|
||||
Target {
|
||||
|
@ -3,8 +3,9 @@ use crate::spec::{base, SanitizerSet, Target};
|
||||
pub fn target() -> Target {
|
||||
let mut base = base::windows_msvc::opts();
|
||||
base.cpu = "x86-64".into();
|
||||
base.features = "+cx16,+sse3,+sahf".into();
|
||||
base.plt_by_default = false;
|
||||
base.max_atomic_width = Some(64);
|
||||
base.max_atomic_width = Some(128);
|
||||
base.supported_sanitizers = SanitizerSet::ADDRESS;
|
||||
|
||||
Target {
|
||||
|
@ -3,6 +3,7 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target};
|
||||
pub fn target() -> Target {
|
||||
let mut base = base::windows_uwp_gnu::opts();
|
||||
base.cpu = "x86-64".into();
|
||||
base.features = "+cx16,+sse3,+sahf".into();
|
||||
base.plt_by_default = false;
|
||||
// Use high-entropy 64 bit address space for ASLR
|
||||
base.add_pre_link_args(
|
||||
@ -10,7 +11,7 @@ pub fn target() -> Target {
|
||||
&["-m", "i386pep", "--high-entropy-va"],
|
||||
);
|
||||
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]);
|
||||
base.max_atomic_width = Some(64);
|
||||
base.max_atomic_width = Some(128);
|
||||
|
||||
Target {
|
||||
llvm_target: "x86_64-pc-windows-gnu".into(),
|
||||
|
@ -3,8 +3,9 @@ use crate::spec::{base, Target};
|
||||
pub fn target() -> Target {
|
||||
let mut base = base::windows_uwp_msvc::opts();
|
||||
base.cpu = "x86-64".into();
|
||||
base.features = "+cx16,+sse3,+sahf".into();
|
||||
base.plt_by_default = false;
|
||||
base.max_atomic_width = Some(64);
|
||||
base.max_atomic_width = Some(128);
|
||||
|
||||
Target {
|
||||
llvm_target: "x86_64-pc-windows-msvc".into(),
|
||||
|
@ -889,7 +889,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage(
|
||||
SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => return self.report_opaque_type_auto_trait_leakage(
|
||||
&obligation,
|
||||
def_id,
|
||||
),
|
||||
@ -2252,8 +2252,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
ErrorCode::E0282,
|
||||
false,
|
||||
);
|
||||
err.stash(span, StashKey::MaybeForgetReturn);
|
||||
return self.dcx().delayed_bug("stashed error never reported");
|
||||
return err.stash(span, StashKey::MaybeForgetReturn).unwrap();
|
||||
}
|
||||
Some(e) => return e,
|
||||
}
|
||||
@ -2766,7 +2765,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
self.suggest_unsized_bound_if_applicable(err, obligation);
|
||||
if let Some(span) = err.span.primary_span()
|
||||
&& let Some(mut diag) =
|
||||
self.tcx.dcx().steal_diagnostic(span, StashKey::AssociatedTypeSuggestion)
|
||||
self.tcx.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion)
|
||||
&& let Ok(ref mut s1) = err.suggestions
|
||||
&& let Ok(ref mut s2) = diag.suggestions
|
||||
{
|
||||
@ -3291,7 +3290,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
def_id: DefId,
|
||||
) -> Diag<'tcx> {
|
||||
) -> ErrorGuaranteed {
|
||||
let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
|
||||
hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
|
||||
"opaque type".to_string()
|
||||
@ -3318,12 +3317,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(diag) = self.dcx().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle)
|
||||
{
|
||||
diag.cancel();
|
||||
}
|
||||
|
||||
err
|
||||
self.note_obligation_cause(&mut err, &obligation);
|
||||
self.point_at_returns_when_relevant(&mut err, &obligation);
|
||||
self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err)
|
||||
}
|
||||
|
||||
fn report_signature_mismatch_error(
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{
|
||||
AtomicBool, AtomicPtr, AtomicU32,
|
||||
AtomicPtr, AtomicU32,
|
||||
Ordering::{AcqRel, Acquire, Relaxed, Release},
|
||||
};
|
||||
use crate::sys::c;
|
||||
@ -9,10 +9,6 @@ use crate::sys::c;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// An optimization hint. The compiler is often smart enough to know if an atomic
|
||||
/// is never set and can remove dead code based on that fact.
|
||||
static HAS_DTORS: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
// Using a per-thread list avoids the problems in synchronizing global state.
|
||||
#[thread_local]
|
||||
#[cfg(target_thread_local)]
|
||||
@ -24,12 +20,11 @@ static DESTRUCTORS: crate::cell::RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut
|
||||
#[inline(never)]
|
||||
#[cfg(target_thread_local)]
|
||||
pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
|
||||
dtors_used();
|
||||
match DESTRUCTORS.try_borrow_mut() {
|
||||
Ok(mut dtors) => dtors.push((t, dtor)),
|
||||
Err(_) => rtabort!("global allocator may not use TLS"),
|
||||
}
|
||||
|
||||
HAS_DTORS.store(true, Relaxed);
|
||||
}
|
||||
|
||||
#[inline(never)] // See comment above
|
||||
@ -130,6 +125,7 @@ impl StaticKey {
|
||||
#[cold]
|
||||
unsafe fn init(&'static self) -> Key {
|
||||
if self.dtor.is_some() {
|
||||
dtors_used();
|
||||
let mut pending = c::FALSE;
|
||||
let r = c::InitOnceBeginInitialize(self.once.get(), 0, &mut pending, ptr::null_mut());
|
||||
assert_eq!(r, c::TRUE);
|
||||
@ -215,7 +211,6 @@ unsafe fn register_dtor(key: &'static StaticKey) {
|
||||
Err(new) => head = new,
|
||||
}
|
||||
}
|
||||
HAS_DTORS.store(true, Release);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -281,17 +276,16 @@ unsafe fn register_dtor(key: &'static StaticKey) {
|
||||
// the address of the symbol to ensure it sticks around.
|
||||
|
||||
#[link_section = ".CRT$XLB"]
|
||||
#[allow(dead_code, unused_variables)]
|
||||
#[used] // we don't want LLVM eliminating this symbol for any reason, and
|
||||
// when the symbol makes it to the linker the linker will take over
|
||||
pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) =
|
||||
on_tls_callback;
|
||||
|
||||
#[allow(dead_code, unused_variables)]
|
||||
unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) {
|
||||
if !HAS_DTORS.load(Acquire) {
|
||||
return;
|
||||
}
|
||||
fn dtors_used() {
|
||||
// we don't want LLVM eliminating p_thread_callback when destructors are used.
|
||||
// when the symbol makes it to the linker the linker will take over
|
||||
unsafe { crate::intrinsics::volatile_load(&p_thread_callback) };
|
||||
}
|
||||
|
||||
unsafe extern "system" fn on_tls_callback(_h: c::LPVOID, dwReason: c::DWORD, _pv: c::LPVOID) {
|
||||
if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH {
|
||||
#[cfg(not(target_thread_local))]
|
||||
run_dtors();
|
||||
@ -301,19 +295,16 @@ unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv:
|
||||
|
||||
// See comments above for what this is doing. Note that we don't need this
|
||||
// trickery on GNU windows, just on MSVC.
|
||||
reference_tls_used();
|
||||
#[cfg(target_env = "msvc")]
|
||||
unsafe fn reference_tls_used() {
|
||||
#[cfg(all(target_env = "msvc", not(target_thread_local)))]
|
||||
{
|
||||
extern "C" {
|
||||
static _tls_used: u8;
|
||||
}
|
||||
crate::intrinsics::volatile_load(&_tls_used);
|
||||
}
|
||||
#[cfg(not(target_env = "msvc"))]
|
||||
unsafe fn reference_tls_used() {}
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // actually called below
|
||||
#[cfg(not(target_thread_local))]
|
||||
unsafe fn run_dtors() {
|
||||
for _ in 0..5 {
|
||||
let mut any_run = false;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_errors::emitter::{DynEmitter, HumanEmitter};
|
||||
use rustc_errors::emitter::{stderr_destination, DynEmitter, HumanEmitter};
|
||||
use rustc_errors::json::JsonEmitter;
|
||||
use rustc_errors::{codes::*, ErrorGuaranteed, TerminalUrl};
|
||||
use rustc_feature::UnstableFeatures;
|
||||
@ -20,6 +20,7 @@ use rustc_span::symbol::sym;
|
||||
use rustc_span::{source_map, Span};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::sync::LazyLock;
|
||||
@ -141,7 +142,7 @@ pub(crate) fn new_dcx(
|
||||
ErrorOutputType::HumanReadable(kind) => {
|
||||
let (short, color_config) = kind.unzip();
|
||||
Box::new(
|
||||
HumanEmitter::stderr(color_config, fallback_bundle)
|
||||
HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
|
||||
.sm(source_map.map(|sm| sm as _))
|
||||
.short_message(short)
|
||||
.teach(unstable_opts.teach)
|
||||
@ -155,24 +156,22 @@ pub(crate) fn new_dcx(
|
||||
Lrc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty()))
|
||||
});
|
||||
Box::new(
|
||||
JsonEmitter::stderr(
|
||||
None,
|
||||
JsonEmitter::new(
|
||||
Box::new(io::BufWriter::new(io::stderr())),
|
||||
source_map,
|
||||
None,
|
||||
fallback_bundle,
|
||||
pretty,
|
||||
json_rendered,
|
||||
diagnostic_width,
|
||||
false,
|
||||
unstable_opts.track_diagnostics,
|
||||
TerminalUrl::No,
|
||||
)
|
||||
.ui_testing(unstable_opts.ui_testing),
|
||||
.ui_testing(unstable_opts.ui_testing)
|
||||
.diagnostic_width(diagnostic_width)
|
||||
.track_diagnostics(unstable_opts.track_diagnostics)
|
||||
.terminal_url(TerminalUrl::No),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
rustc_errors::DiagCtxt::with_emitter(emitter).with_flags(unstable_opts.dcx_flags(true))
|
||||
rustc_errors::DiagCtxt::new(emitter).with_flags(unstable_opts.dcx_flags(true))
|
||||
}
|
||||
|
||||
/// Parse, resolve, and typecheck the given crate.
|
||||
|
@ -1,6 +1,7 @@
|
||||
use rustc_ast as ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::emitter::stderr_destination;
|
||||
use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError};
|
||||
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
||||
use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID};
|
||||
@ -576,14 +577,14 @@ pub(crate) fn make_test(
|
||||
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
|
||||
false,
|
||||
);
|
||||
supports_color = HumanEmitter::stderr(ColorConfig::Auto, fallback_bundle.clone())
|
||||
.diagnostic_width(Some(80))
|
||||
.supports_color();
|
||||
supports_color =
|
||||
HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
|
||||
.supports_color();
|
||||
|
||||
let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
|
||||
|
||||
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
|
||||
let dcx = DiagCtxt::with_emitter(Box::new(emitter)).disable_warnings();
|
||||
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
|
||||
let sess = ParseSess::with_dcx(dcx, sm);
|
||||
|
||||
let mut found_main = false;
|
||||
@ -768,7 +769,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
|
||||
|
||||
let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
|
||||
|
||||
let dcx = DiagCtxt::with_emitter(Box::new(emitter)).disable_warnings();
|
||||
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
|
||||
let sess = ParseSess::with_dcx(dcx, sm);
|
||||
let mut parser =
|
||||
match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) {
|
||||
|
@ -42,7 +42,7 @@ fn check_rust_syntax(
|
||||
let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle };
|
||||
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let dcx = DiagCtxt::with_emitter(Box::new(emitter)).disable_warnings();
|
||||
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
|
||||
let source = dox[code_block.code].to_owned();
|
||||
let sess = ParseSess::with_dcx(dcx, sm);
|
||||
|
||||
|
@ -45,7 +45,7 @@ pub fn check(
|
||||
let fallback_bundle =
|
||||
rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
|
||||
let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
|
||||
let dcx = DiagCtxt::with_emitter(Box::new(emitter)).disable_warnings();
|
||||
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
|
||||
#[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_dcx
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let sess = ParseSess::with_dcx(dcx, sm);
|
||||
|
@ -0,0 +1,8 @@
|
||||
struct Foo(isize, isize, isize, isize);
|
||||
|
||||
pub fn main() {
|
||||
let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5);
|
||||
match [5, 5, 5, 5] {
|
||||
[..] => { }
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions
|
||||
--> tests/ui/crashes/unreachable-array-or-slice.rs:4:9
|
||||
|
|
||||
LL | let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5);
|
||||
| ^^^^ `Self` is only available in impls, traits, and type definitions
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0433`.
|
@ -68,7 +68,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
||||
queries: &'tcx rustc_interface::Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
queries.global_ctxt().unwrap().enter(|tcx| {
|
||||
if tcx.sess.dcx().has_errors().is_some() {
|
||||
if tcx.sess.dcx().has_errors_or_delayed_bugs().is_some() {
|
||||
tcx.dcx().fatal("miri cannot be run on programs that fail compilation");
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ fn format_project<T: FormatHandler>(
|
||||
let main_file = input.file_name();
|
||||
let input_is_stdin = main_file == FileName::Stdin;
|
||||
|
||||
let mut parse_session = ParseSess::new(config)?;
|
||||
let parse_session = ParseSess::new(config)?;
|
||||
if config.skip_children() && parse_session.ignore_file(&main_file) {
|
||||
return Ok(FormatReport::new());
|
||||
}
|
||||
@ -118,20 +118,11 @@ fn format_project<T: FormatHandler>(
|
||||
let mut report = FormatReport::new();
|
||||
let directory_ownership = input.to_directory_ownership();
|
||||
|
||||
// rustfmt doesn't use `run_compiler` like other tools, so it must emit any
|
||||
// stashed diagnostics itself, otherwise the `DiagCtxt` will assert when
|
||||
// dropped. The final result here combines the parsing result and the
|
||||
// `emit_stashed_diagnostics` result.
|
||||
let parse_res = Parser::parse_crate(input, &parse_session);
|
||||
let stashed_res = parse_session.emit_stashed_diagnostics();
|
||||
let krate = match (parse_res, stashed_res) {
|
||||
(Ok(krate), None) => krate,
|
||||
(parse_res, _) => {
|
||||
// Surface parse error via Session (errors are merged there from report).
|
||||
let forbid_verbose = match parse_res {
|
||||
Err(e) if e != ParserError::ParsePanicError => true,
|
||||
_ => input_is_stdin,
|
||||
};
|
||||
let krate = match Parser::parse_crate(input, &parse_session) {
|
||||
Ok(krate) => krate,
|
||||
// Surface parse error via Session (errors are merged there from report)
|
||||
Err(e) => {
|
||||
let forbid_verbose = input_is_stdin || e != ParserError::ParsePanicError;
|
||||
should_emit_verbose(forbid_verbose, config, || {
|
||||
eprintln!("The Rust parser panicked");
|
||||
});
|
||||
|
@ -3,11 +3,9 @@ use std::path::Path;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
|
||||
use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter};
|
||||
use rustc_errors::emitter::{stderr_destination, DynEmitter, Emitter, HumanEmitter};
|
||||
use rustc_errors::translation::Translate;
|
||||
use rustc_errors::{
|
||||
ColorConfig, Diag, DiagCtxt, DiagInner, ErrorGuaranteed, Level as DiagnosticLevel,
|
||||
};
|
||||
use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
|
||||
use rustc_session::parse::ParseSess as RawParseSess;
|
||||
use rustc_span::{
|
||||
source_map::{FilePathMapping, SourceMap},
|
||||
@ -152,9 +150,12 @@ fn default_dcx(
|
||||
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
|
||||
false,
|
||||
);
|
||||
Box::new(HumanEmitter::stderr(emit_color, fallback_bundle).sm(Some(source_map.clone())))
|
||||
Box::new(
|
||||
HumanEmitter::new(stderr_destination(emit_color), fallback_bundle)
|
||||
.sm(Some(source_map.clone())),
|
||||
)
|
||||
};
|
||||
DiagCtxt::with_emitter(Box::new(SilentOnIgnoredFilesEmitter {
|
||||
DiagCtxt::new(Box::new(SilentOnIgnoredFilesEmitter {
|
||||
has_non_ignorable_parser_errors: false,
|
||||
source_map,
|
||||
emitter,
|
||||
@ -230,12 +231,8 @@ impl ParseSess {
|
||||
self.ignore_path_set.as_ref().is_match(path)
|
||||
}
|
||||
|
||||
pub(crate) fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
|
||||
self.parse_sess.dcx.emit_stashed_diagnostics()
|
||||
}
|
||||
|
||||
pub(crate) fn set_silent_emitter(&mut self) {
|
||||
self.parse_sess.dcx = DiagCtxt::with_emitter(silent_emitter());
|
||||
self.parse_sess.dcx = DiagCtxt::new(silent_emitter());
|
||||
}
|
||||
|
||||
pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
|
||||
|
@ -12,4 +12,4 @@ pub unsafe fn crc32sse(v: u8) -> u32 {
|
||||
_mm_crc32_u8(out, v)
|
||||
}
|
||||
|
||||
// CHECK: attributes #0 {{.*"target-features"="\+sse4.2,\+crc32"}}
|
||||
// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32"}}
|
||||
|
4
tests/ui/foreign/stashed-issue-121451.rs
Normal file
4
tests/ui/foreign/stashed-issue-121451.rs
Normal file
@ -0,0 +1,4 @@
|
||||
extern "C" fn _f() -> libc::uintptr_t {}
|
||||
//~^ ERROR failed to resolve: use of undeclared crate or module `libc`
|
||||
|
||||
fn main() {}
|
9
tests/ui/foreign/stashed-issue-121451.stderr
Normal file
9
tests/ui/foreign/stashed-issue-121451.stderr
Normal file
@ -0,0 +1,9 @@
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `libc`
|
||||
--> $DIR/stashed-issue-121451.rs:1:23
|
||||
|
|
||||
LL | extern "C" fn _f() -> libc::uintptr_t {}
|
||||
| ^^^^ use of undeclared crate or module `libc`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0433`.
|
13
tests/ui/impl-trait/stashed-diag-issue-121504.rs
Normal file
13
tests/ui/impl-trait/stashed-diag-issue-121504.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//@ edition: 2021
|
||||
|
||||
trait MyTrait {
|
||||
async fn foo(self) -> (Self, i32);
|
||||
}
|
||||
|
||||
impl MyTrait for xyz::T { //~ ERROR failed to resolve: use of undeclared crate or module `xyz`
|
||||
async fn foo(self, key: i32) -> (u32, i32) {
|
||||
(self, key)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
9
tests/ui/impl-trait/stashed-diag-issue-121504.stderr
Normal file
9
tests/ui/impl-trait/stashed-diag-issue-121504.stderr
Normal file
@ -0,0 +1,9 @@
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `xyz`
|
||||
--> $DIR/stashed-diag-issue-121504.rs:7:18
|
||||
|
|
||||
LL | impl MyTrait for xyz::T {
|
||||
| ^^^ use of undeclared crate or module `xyz`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0433`.
|
@ -3,7 +3,7 @@
|
||||
use std::ptr::addr_of;
|
||||
|
||||
const UNINHABITED_VARIANT: () = unsafe {
|
||||
let v = *addr_of!(data).cast(); //~ ERROR cannot determine resolution for the macro `addr_of`
|
||||
let v = *addr_of!(data).cast();
|
||||
};
|
||||
|
||||
fn main() {}
|
||||
|
@ -13,13 +13,5 @@ LL - #![derive(Clone, Copy)]
|
||||
LL + #[derive(Clone, Copy)]
|
||||
|
|
||||
|
||||
error: cannot determine resolution for the macro `addr_of`
|
||||
--> $DIR/issue-121108.rs:6:14
|
||||
|
|
||||
LL | let v = *addr_of!(data).cast();
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: import resolution is stuck, try simplifying macro imports
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -7,5 +7,7 @@ trait Has {
|
||||
trait HasNot {}
|
||||
|
||||
fn main() {
|
||||
HasNot::has(); //~ ERROR
|
||||
HasNot::has();
|
||||
//~^ ERROR trait objects must include the `dyn` keyword
|
||||
//~| ERROR no function or associated item named `has` found for trait `HasNot`
|
||||
}
|
||||
|
@ -1,3 +1,14 @@
|
||||
error[E0782]: trait objects must include the `dyn` keyword
|
||||
--> $DIR/issue-111312.rs:10:5
|
||||
|
|
||||
LL | HasNot::has();
|
||||
| ^^^^^^
|
||||
|
|
||||
help: add `dyn` keyword before this trait
|
||||
|
|
||||
LL | <dyn HasNot>::has();
|
||||
| ++++ +
|
||||
|
||||
error[E0599]: no function or associated item named `has` found for trait `HasNot`
|
||||
--> $DIR/issue-111312.rs:10:13
|
||||
|
|
||||
@ -10,6 +21,7 @@ note: `Has` defines an item `has`
|
||||
LL | trait Has {
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
||||
Some errors have detailed explanations: E0599, E0782.
|
||||
For more information about an error, try `rustc --explain E0599`.
|
||||
|
@ -1,5 +1,7 @@
|
||||
//@ edition: 2021
|
||||
|
||||
fn main() {
|
||||
std::any::Any::create(); //~ ERROR
|
||||
std::any::Any::create();
|
||||
//~^ ERROR trait objects must include the `dyn` keyword
|
||||
//~| ERROR no function or associated item named `create` found for trait `Any`
|
||||
}
|
||||
|
@ -1,9 +1,21 @@
|
||||
error[E0782]: trait objects must include the `dyn` keyword
|
||||
--> $DIR/issue-111727.rs:4:5
|
||||
|
|
||||
LL | std::any::Any::create();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: add `dyn` keyword before this trait
|
||||
|
|
||||
LL | <dyn std::any::Any>::create();
|
||||
| ++++ +
|
||||
|
||||
error[E0599]: no function or associated item named `create` found for trait `Any`
|
||||
--> $DIR/issue-111727.rs:4:20
|
||||
|
|
||||
LL | std::any::Any::create();
|
||||
| ^^^^^^ function or associated item not found in `Any`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
||||
Some errors have detailed explanations: E0599, E0782.
|
||||
For more information about an error, try `rustc --explain E0599`.
|
||||
|
@ -11,6 +11,7 @@ fn foo<'a, 'b>() -> Tait<'a> {
|
||||
}
|
||||
let x: Tait<'a> = ();
|
||||
x
|
||||
//~^ ERROR concrete type differs from previous defining opaque type use
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,3 +1,15 @@
|
||||
error: concrete type differs from previous defining opaque type use
|
||||
--> $DIR/different_defining_uses_never_type-2.rs:13:5
|
||||
|
|
||||
LL | x
|
||||
| ^ expected `i32`, got `()`
|
||||
|
|
||||
note: previous use here
|
||||
--> $DIR/different_defining_uses_never_type-2.rs:8:31
|
||||
|
|
||||
LL | let y: Tait<'b> = 1i32;
|
||||
| ^^^^
|
||||
|
||||
error: concrete type differs from previous defining opaque type use
|
||||
--> $DIR/different_defining_uses_never_type-2.rs:8:31
|
||||
|
|
||||
@ -10,5 +22,5 @@ note: previous use here
|
||||
LL | if { return } {
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user