diff options
139 files changed, 1962 insertions, 3447 deletions
diff --git a/RELEASES.md b/RELEASES.md index 3fb74b52292..038a83cde84 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,96 @@ +Version 1.76.0 (2024-02-08) +========================== + +<a id="1.76.0-Language"></a> + +Language +-------- +- [Document Rust ABI compatibility between various types](https://github.com/rust-lang/rust/pull/115476/) +- [Also: guarantee that char and u32 are ABI-compatible](https://github.com/rust-lang/rust/pull/118032/) +- [Warn against ambiguous wide pointer comparisons](https://github.com/rust-lang/rust/pull/117758/) + +<a id="1.76.0-Compiler"></a> + +Compiler +-------- +- [Lint pinned `#[must_use]` pointers (in particular, `Box<T>` where `T` is `#[must_use]`) in `unused_must_use`.](https://github.com/rust-lang/rust/pull/118054/) +- [Soundness fix: fix computing the offset of an unsized field in a packed struct](https://github.com/rust-lang/rust/pull/118540/) +- [Soundness fix: fix dynamic size/align computation logic for packed types with dyn Trait tail](https://github.com/rust-lang/rust/pull/118538/) +- [Add `$message_type` field to distinguish json diagnostic outputs](https://github.com/rust-lang/rust/pull/115691/) +- [Enable Rust to use the EHCont security feature of Windows](https://github.com/rust-lang/rust/pull/118013/) +- [Add tier 3 {x86_64,i686}-win7-windows-msvc targets](https://github.com/rust-lang/rust/pull/118150/) +- [Add tier 3 aarch64-apple-watchos target](https://github.com/rust-lang/rust/pull/119074/) +- [Add tier 3 arm64e-apple-ios & arm64e-apple-darwin targets](https://github.com/rust-lang/rust/pull/115526/) + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +<a id="1.76.0-Libraries"></a> + +Libraries +--------- +- [Add a column number to `dbg!()`](https://github.com/rust-lang/rust/pull/114962/) +- [Add `std::hash::{DefaultHasher, RandomState}` exports](https://github.com/rust-lang/rust/pull/115694/) +- [Fix rounding issue with exponents in fmt](https://github.com/rust-lang/rust/pull/116301/) +- [Add T: ?Sized to `RwLockReadGuard` and `RwLockWriteGuard`'s Debug impls.](https://github.com/rust-lang/rust/pull/117138/) +- [Windows: Allow `File::create` to work on hidden files](https://github.com/rust-lang/rust/pull/116438/) + +<a id="1.76.0-Stabilized-APIs"></a> + +Stabilized APIs +--------------- + +- [`Arc::unwrap_or_clone`](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.unwrap_or_clone) +- [`Rc::unwrap_or_clone`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.unwrap_or_clone) +- [`Result::inspect`](https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.inspect) +- [`Result::inspect_err`](https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.inspect_err) +- [`Option::inspect`](https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.inspect) +- [`type_name_of_val`](https://doc.rust-lang.org/stable/std/any/fn.type_name_of_val.html) +- [`std::hash::{DefaultHasher, RandomState}`](https://doc.rust-lang.org/stable/std/hash/index.html#structs) + These were previously available only through `std::collections::hash_map`. +- [`ptr::{from_ref, from_mut}`](https://doc.rust-lang.org/stable/std/ptr/fn.from_ref.html) +- [`ptr::addr_eq`](https://doc.rust-lang.org/stable/std/ptr/fn.addr_eq.html) + +<a id="1.76.0-Cargo"></a> + +Cargo +----- + +See [Cargo release notes](https://github.com/rust-lang/cargo/blob/master/CHANGELOG.md#cargo-176-2024-02-08). + +<a id="1.76.0-Rustdoc"></a> + +Rustdoc +------- + +- [Don't merge cfg and doc(cfg) attributes for re-exports](https://github.com/rust-lang/rust/pull/113091/) +- [rustdoc: allow resizing the sidebar / hiding the top bar](https://github.com/rust-lang/rust/pull/115660/) +- [rustdoc-search: add support for traits and associated types](https://github.com/rust-lang/rust/pull/116085/) +- [rustdoc: Add highlighting for comments in items declaration](https://github.com/rust-lang/rust/pull/117869/) + +<a id="1.76.0-Compatibility-Notes"></a> + +Compatibility Notes +------------------- +- [Add allow-by-default lint for unit bindings](https://github.com/rust-lang/rust/pull/112380/) + This is expected to be upgraded to a warning by default in a future Rust + release. Some macros emit bindings with type `()` with user-provided spans, + which means that this lint will warn for user code. +- [Remove x86_64-sun-solaris target.](https://github.com/rust-lang/rust/pull/118091/) +- [Remove asmjs-unknown-emscripten target](https://github.com/rust-lang/rust/pull/117338/) +- [Report errors in jobserver inherited through environment variables](https://github.com/rust-lang/rust/pull/113730/) + This [may warn](https://github.com/rust-lang/rust/issues/120515) on benign problems too. +- [Update the minimum external LLVM to 16.](https://github.com/rust-lang/rust/pull/117947/) +- [Improve `print_tts`](https://github.com/rust-lang/rust/pull/114571/) + This change can break some naive manual parsing of token trees in proc macro + code which expect a particular structure after `.to_string()`, rather than just arbitrary Rust code. +- [Make `IMPLIED_BOUNDS_ENTAILMENT` into a hard error from a lint](https://github.com/rust-lang/rust/pull/117984/) +- [Vec's allocation behavior was changed when collecting some iterators](https://github.com/rust-lang/rust/pull/110353) + Allocation behavior is currently not specified, nevertheless changes can be surprising. + See [`impl FromIterator for Vec`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#impl-FromIterator%3CT%3E-for-Vec%3CT%3E) + for more details. +- [Properly reject `default` on free const items](https://github.com/rust-lang/rust/pull/117818/) + Version 1.75.0 (2023-12-28) ========================== diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 0a6b758efa5..5eb85d59fee 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -198,12 +198,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { { let span = self.body.local_decls[local].source_info.span; mut_error = Some(span); - if let Some((buffer, c)) = self.get_buffered_mut_error(span) { + if let Some((buffered_err, c)) = self.get_buffered_mut_error(span) { // We've encountered a second (or more) attempt to mutably borrow an // immutable binding, so the likely problem is with the binding // declaration, not the use. We collect these in a single diagnostic // and make the binding the primary span of the error. - err = buffer; + err = buffered_err; count = c + 1; if count == 2 { err.replace_span_with(span, false); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 03f8f43ff16..f8f054db65e 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -408,7 +408,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool, target_info: &Locke .filter(|_feature| { target_info.cpu_supports(_feature) /* - adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512ifma, + adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma, avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq, bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm, sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index d82ff6656f4..e839d278bea 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -102,7 +102,7 @@ pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>); impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for ParseTargetMachineConfig<'_> { fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> { let diag: DiagnosticBuilder<'_, G> = self.0.into_diagnostic(dcx, level); - let (message, _) = diag.messages().first().expect("`LlvmError` with no message"); + let (message, _) = diag.messages.first().expect("`LlvmError` with no message"); let message = dcx.eagerly_translate_to_string(message.clone(), diag.args()); DiagnosticBuilder::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 06edb794537..4211f875dd0 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -39,7 +39,6 @@ use rustc_target::spec::{MergeFunctions, SanitizerSet}; use crate::errors::ErrorCreatingRemarkDir; use std::any::Any; -use std::borrow::Cow; use std::fs; use std::io; use std::marker::PhantomData; @@ -1812,12 +1811,12 @@ impl Translate for SharedEmitter { impl Emitter for SharedEmitter { fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) { - let args: FxHashMap<Cow<'_, str>, DiagnosticArgValue> = + let args: FxHashMap<DiagnosticArgName, DiagnosticArgValue> = diag.args().map(|(name, arg)| (name.clone(), arg.clone())).collect(); drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { msgs: diag.messages.clone(), args: args.clone(), - code: diag.code.clone(), + code: diag.code, lvl: diag.level(), }))); for child in &diag.children { diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index dabf78ef90e..1ebc82ad6d4 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -1,6 +1,8 @@ use std::mem; -use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, IntoDiagnostic, IntoDiagnosticArg}; +use rustc_errors::{ + DiagnosticArgName, DiagnosticArgValue, DiagnosticMessage, IntoDiagnostic, IntoDiagnosticArg, +}; use rustc_hir::CRATE_HIR_ID; use rustc_middle::mir::AssertKind; use rustc_middle::query::TyCtxtAt; @@ -32,10 +34,7 @@ impl MachineStopType for ConstEvalErrKind { AssertFailure(x) => x.diagnostic_message(), } } - fn add_args( - self: Box<Self>, - adder: &mut dyn FnMut(std::borrow::Cow<'static, str>, DiagnosticArgValue), - ) { + fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagnosticArgName, DiagnosticArgValue)) { use ConstEvalErrKind::*; match *self { ConstAccessesStatic | ModifiedGlobal => {} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index b87eef07fd5..8ad4925cff2 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -33,7 +33,10 @@ pub type DiagnosticArgName = Cow<'static, str>; #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub enum DiagnosticArgValue { Str(Cow<'static, str>), - Number(i128), + // This gets converted to a `FluentNumber`, which is an `f64`. An `i32` + // safely fits in an `f64`. Any integers bigger than that will be converted + // to strings in `into_diagnostic_arg` and stored using the `Str` variant. + Number(i32), StrListSepByAnd(Vec<Cow<'static, str>>), } @@ -113,7 +116,7 @@ pub struct Diagnostic { /// With `-Ztrack_diagnostics` enabled, /// we print where in rustc this error was emitted. - pub emitted_at: DiagnosticLocation, + pub(crate) emitted_at: DiagnosticLocation, } #[derive(Clone, Debug, Encodable, Decodable)] @@ -162,10 +165,10 @@ impl DiagnosticStyledString { DiagnosticStyledString(vec![]) } pub fn push_normal<S: Into<String>>(&mut self, t: S) { - self.0.push(StringPart::Normal(t.into())); + self.0.push(StringPart::normal(t)); } pub fn push_highlighted<S: Into<String>>(&mut self, t: S) { - self.0.push(StringPart::Highlighted(t.into())); + self.0.push(StringPart::highlighted(t)); } pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) { if highlight { @@ -175,35 +178,34 @@ impl DiagnosticStyledString { } } pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString { - DiagnosticStyledString(vec![StringPart::Normal(t.into())]) + DiagnosticStyledString(vec![StringPart::normal(t)]) } pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString { - DiagnosticStyledString(vec![StringPart::Highlighted(t.into())]) + DiagnosticStyledString(vec![StringPart::highlighted(t)]) } pub fn content(&self) -> String { - self.0.iter().map(|x| x.content()).collect::<String>() + self.0.iter().map(|x| x.content.as_str()).collect::<String>() } } #[derive(Debug, PartialEq, Eq)] -pub enum StringPart { - Normal(String), - Highlighted(String), +pub struct StringPart { + content: String, + style: Style, } impl StringPart { - pub fn content(&self) -> &str { - match self { - &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s, - } + pub fn normal<S: Into<String>>(content: S) -> StringPart { + StringPart { content: content.into(), style: Style::NoStyle } + } + + pub fn highlighted<S: Into<String>>(content: S) -> StringPart { + StringPart { content: content.into(), style: Style::Highlight } } } -// Note: most of these methods are setters that return `&mut Self`. The small -// number of simple getter functions all have `get_` prefixes to distinguish -// them from the setters. impl Diagnostic { #[track_caller] pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self { @@ -389,19 +391,16 @@ impl Diagnostic { } else { (0, found_label.len() - expected_label.len()) }; - let mut msg: Vec<_> = - vec![(format!("{}{} `", " ".repeat(expected_padding), expected_label), Style::NoStyle)]; - msg.extend(expected.0.iter().map(|x| match *x { - StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), - StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), - })); - msg.push((format!("`{expected_extra}\n"), Style::NoStyle)); - msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle)); - msg.extend(found.0.iter().map(|x| match *x { - StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), - StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), - })); - msg.push((format!("`{found_extra}"), Style::NoStyle)); + let mut msg = vec![StringPart::normal(format!( + "{}{} `", + " ".repeat(expected_padding), + expected_label + ))]; + msg.extend(expected.0.into_iter()); + msg.push(StringPart::normal(format!("`{expected_extra}\n"))); + msg.push(StringPart::normal(format!("{}{} `", " ".repeat(found_padding), found_label))); + msg.extend(found.0.into_iter()); + msg.push(StringPart::normal(format!("`{found_extra}"))); // For now, just attach these as notes. self.highlighted_note(msg); @@ -410,9 +409,9 @@ impl Diagnostic { pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self { self.highlighted_note(vec![ - (format!("`{name}` from trait: `"), Style::NoStyle), - (signature, Style::Highlight), - ("`".to_string(), Style::NoStyle), + StringPart::normal(format!("`{name}` from trait: `")), + StringPart::highlighted(signature), + StringPart::normal("`"), ]); self } @@ -424,10 +423,7 @@ impl Diagnostic { self } - fn highlighted_note<M: Into<SubdiagnosticMessage>>( - &mut self, - msg: Vec<(M, Style)>, - ) -> &mut Self { + fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self { self.sub_with_highlights(Level::Note, msg, MultiSpan::new()); self } @@ -496,7 +492,7 @@ impl Diagnostic { } /// Add a help message attached to this diagnostic with a customizable highlighted message. - pub fn highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self { + pub fn highlighted_help(&mut self, msg: Vec<StringPart>) -> &mut Self { self.sub_with_highlights(Level::Help, msg, MultiSpan::new()); self } @@ -890,15 +886,6 @@ impl Diagnostic { self } - pub fn clear_code(&mut self) -> &mut Self { - self.code = None; - self - } - - pub fn get_code(&self) -> Option<ErrCode> { - self.code - } - pub fn primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.messages[0] = (msg.into(), Style::NoStyle); self @@ -913,7 +900,7 @@ impl Diagnostic { pub fn arg( &mut self, - name: impl Into<Cow<'static, str>>, + name: impl Into<DiagnosticArgName>, arg: impl IntoDiagnosticArg, ) -> &mut Self { self.args.insert(name.into(), arg.into_diagnostic_arg()); @@ -924,10 +911,6 @@ impl Diagnostic { self.args = args; } - pub fn messages(&self) -> &[(DiagnosticMessage, Style)] { - &self.messages - } - /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by /// combining it with the primary message of the diagnostic (if translatable, otherwise it just /// passes the user's string along). @@ -958,15 +941,10 @@ impl Diagnostic { /// Convenience function for internal use, clients should use one of the /// public methods above. - fn sub_with_highlights<M: Into<SubdiagnosticMessage>>( - &mut self, - level: Level, - messages: Vec<(M, Style)>, - span: MultiSpan, - ) { + fn sub_with_highlights(&mut self, level: Level, messages: Vec<StringPart>, span: MultiSpan) { let messages = messages .into_iter() - .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1)) + .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.content), m.style)) .collect(); let sub = SubDiagnostic { level, messages, span }; self.children.push(sub); diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index faab3fc663a..15effd3cbec 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -63,12 +63,8 @@ macro_rules! into_diagnostic_arg_for_number { $( impl IntoDiagnosticArg for $ty { fn into_diagnostic_arg(self) -> DiagnosticArgValue { - // HACK: `FluentNumber` the underline backing struct represent - // numbers using a f64 which can't represent all the i128 numbers - // So in order to be able to use fluent selectors and still - // have all the numbers representable we only convert numbers - // below a certain threshold. - if let Ok(n) = TryInto::<i128>::try_into(self) && n >= -100 && n <= 100 { + // Convert to a string if it won't fit into `Number`. + if let Ok(n) = TryInto::<i32>::try_into(self) { DiagnosticArgValue::Number(n) } else { self.to_string().into_diagnostic_arg() diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 9f76c1dd248..4be5ed923e5 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -558,7 +558,7 @@ impl Emitter for HumanEmitter { /// failures of rustc, as witnessed e.g. in issue #89358. pub struct SilentEmitter { pub fatal_dcx: DiagCtxt, - pub fatal_note: Option<String>, + pub fatal_note: String, } impl Translate for SilentEmitter { @@ -576,13 +576,11 @@ impl Emitter for SilentEmitter { None } - fn emit_diagnostic(&mut self, d: &Diagnostic) { - if d.level == Level::Fatal { - let mut d = d.clone(); - if let Some(ref note) = self.fatal_note { - d.note(note.clone()); - } - self.fatal_dcx.emit_diagnostic(d); + fn emit_diagnostic(&mut self, diag: &Diagnostic) { + if diag.level == Level::Fatal { + let mut diag = diag.clone(); + diag.note(self.fatal_note.clone()); + self.fatal_dcx.emit_diagnostic(diag); } } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 960b68196ff..b2bd4d8eb95 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -34,7 +34,7 @@ extern crate self as rustc_errors; pub use codes::*; pub use diagnostic::{ AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgName, - DiagnosticArgValue, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, + DiagnosticArgValue, DiagnosticStyledString, IntoDiagnosticArg, StringPart, SubDiagnostic, }; pub use diagnostic_builder::{ BugAbort, DiagnosticBuilder, EmissionGuarantee, FatalAbort, IntoDiagnostic, @@ -102,7 +102,6 @@ pub type PResult<'a, T> = Result<T, PErr<'a>>; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. -// (See also the comment on `DiagnosticBuilderInner`'s `diagnostic` field.) #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16); #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] @@ -1039,10 +1038,6 @@ impl DiagCtxt { } } - pub fn take_future_breakage_diagnostics(&self) -> Vec<Diagnostic> { - std::mem::take(&mut self.inner.borrow_mut().future_breakage_diagnostics) - } - pub fn abort_if_errors(&self) { let mut inner = self.inner.borrow_mut(); inner.emit_stashed_diagnostics(); @@ -1150,8 +1145,12 @@ impl DiagCtxt { self.inner.borrow_mut().emitter.emit_artifact_notification(path, artifact_type); } - pub fn emit_future_breakage_report(&self, diags: Vec<Diagnostic>) { - self.inner.borrow_mut().emitter.emit_future_breakage_report(diags) + pub fn emit_future_breakage_report(&self) { + let mut inner = self.inner.borrow_mut(); + let diags = std::mem::take(&mut inner.future_breakage_diagnostics); + if !diags.is_empty() { + inner.emitter.emit_future_breakage_report(diags); + } } pub fn emit_unused_externs( @@ -1224,9 +1223,8 @@ impl DiagCtxtInner { /// Emit all stashed diagnostics. fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> { let has_errors = self.has_errors(); - let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::<Vec<_>>(); let mut reported = None; - for diag in diags { + for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { // Decrement the count tracking the stash; emitting will increment it. if diag.is_error() { if diag.is_lint.is_some() { @@ -1254,7 +1252,7 @@ impl DiagCtxtInner { // be stored. Instead, they are buffered until the `LintExpectationId` is replaced by // a stable one by the `LintLevelsBuilder`. if let Some(LintExpectationId::Unstable { .. }) = diagnostic.level.get_expectation_id() { - self.unstable_expect_diagnostics.push(diagnostic.clone()); + self.unstable_expect_diagnostics.push(diagnostic); return None; } @@ -1269,16 +1267,14 @@ impl DiagCtxtInner { DelayedBug(DelayedBugKind::Normal) => { let backtrace = std::backtrace::Backtrace::capture(); self.span_delayed_bugs - .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); - + .push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); #[allow(deprecated)] return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); } DelayedBug(DelayedBugKind::GoodPath) => { let backtrace = std::backtrace::Backtrace::capture(); self.good_path_delayed_bugs - .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); - + .push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); return None; } _ => {} @@ -1424,7 +1420,7 @@ impl DiagCtxtInner { &mut out, "delayed span bug: {}\n{}\n", bug.inner - .messages() + .messages .iter() .filter_map(|(msg, _)| msg.as_str()) .collect::<String>(), diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index 98eb70b5fce..b55f7853885 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -197,7 +197,7 @@ pub struct StyledString { pub style: Style, } -#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub enum Style { MainHeaderMsg, HeaderMsg, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 3674a760cbf..5cdcc1bb860 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -590,7 +590,6 @@ fn infer_placeholder_type<'a>( // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type. // We are typeck and have the real type, so remove that and suggest the actual type. - // FIXME(eddyb) this looks like it should be functionality on `Diagnostic`. if let Ok(suggestions) = &mut err.suggestions { suggestions.clear(); } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index b7f28ef958a..fcf4b59e93f 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -576,7 +576,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if (lhs, rhs).references_error() { err.downgrade_to_delayed_bug(); } - if self.tcx.sess.teach(err.get_code().unwrap()) { + if self.tcx.sess.teach(err.code.unwrap()) { err.note( "In a match expression, only numbers and characters can be matched \ against a range. This is because the compiler checks that the range \ @@ -847,7 +847,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { type_str ); err.span_label(span, format!("type `{type_str}` cannot be dereferenced")); - if self.tcx.sess.teach(err.get_code().unwrap()) { + if self.tcx.sess.teach(err.code.unwrap()) { err.note(CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ); } return Err(err.emit()); @@ -1669,7 +1669,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if tcx.sess.teach(err.get_code().unwrap()) { + if tcx.sess.teach(err.code.unwrap()) { err.note( "This error indicates that a struct pattern attempted to \ extract a nonexistent field from a struct. Struct fields \ diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 0e7c641e0e0..0452d4fe6c8 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -195,7 +195,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } diag.help("type parameters must be constrained to match other types"); - if tcx.sess.teach(diag.get_code().unwrap()) { + if tcx.sess.teach(diag.code.unwrap()) { diag.help( "given a type parameter `T` and a method `foo`: ``` @@ -678,7 +678,7 @@ impl<T> Trait<T> for X { https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", ); } - if tcx.sess.teach(diag.get_code().unwrap()) { + if tcx.sess.teach(diag.code.unwrap()) { diag.help( "given an associated type `T` and a method `foo`: ``` diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 5ca88090996..8a4705e0056 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -45,20 +45,19 @@ pub struct Compiler { pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec<String>) -> Cfg { cfgs.into_iter() .map(|s| { - let sess = ParseSess::with_silent_emitter(Some(format!( + let sess = ParseSess::with_silent_emitter(format!( "this error occurred on the command line: `--cfg={s}`" - ))); + )); let filename = FileName::cfg_spec_source_code(&s); macro_rules! error { ($reason: expr) => { #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] - dcx.struct_fatal(format!( + dcx.fatal(format!( concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s - )) - .emit(); + )); }; } @@ -108,20 +107,19 @@ pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec<String>) -> CheckCfg { let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() }; for s in specs { - let sess = ParseSess::with_silent_emitter(Some(format!( + let sess = ParseSess::with_silent_emitter(format!( "this error occurred on the command line: `--check-cfg={s}`" - ))); + )); let filename = FileName::cfg_spec_source_code(&s); macro_rules! error { ($reason:expr) => { #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] - dcx.struct_fatal(format!( + dcx.fatal(format!( concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"), s )) - .emit() }; } diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 211da800202..3ebbcd65030 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -1,7 +1,6 @@ -use std::borrow::Cow; use std::fmt; -use rustc_errors::{codes::*, DiagnosticArgValue, DiagnosticMessage}; +use rustc_errors::{codes::*, DiagnosticArgName, DiagnosticArgValue, DiagnosticMessage}; use rustc_macros::Diagnostic; use rustc_span::{Span, Symbol}; @@ -95,14 +94,14 @@ pub(super) struct ConstNotUsedTraitAlias { pub struct CustomSubdiagnostic<'a> { pub msg: fn() -> DiagnosticMessage, - pub add_args: Box<dyn FnOnce(&mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue)) + 'a>, + pub add_args: Box<dyn FnOnce(&mut dyn FnMut(DiagnosticArgName, DiagnosticArgValue)) + 'a>, } impl<'a> CustomSubdiagnostic<'a> { pub fn label(x: fn() -> DiagnosticMessage) -> Self { Self::label_and_then(x, |_| {}) } - pub fn label_and_then<F: FnOnce(&mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue)) + 'a>( + pub fn label_and_then<F: FnOnce(&mut dyn FnMut(DiagnosticArgName, DiagnosticArgValue)) + 'a>( msg: fn() -> DiagnosticMessage, f: F, ) -> Self { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 95574aee499..0f69ab93452 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -5,7 +5,9 @@ use crate::mir::{ConstAlloc, ConstValue}; use crate::ty::{layout, tls, Ty, TyCtxt, ValTree}; use rustc_data_structures::sync::Lock; -use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg}; +use rustc_errors::{ + DiagnosticArgName, DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg, +}; use rustc_macros::HashStable; use rustc_session::CtfeBacktrace; use rustc_span::{def_id::DefId, Span, DUMMY_SP}; @@ -485,7 +487,7 @@ pub trait MachineStopType: Any + fmt::Debug + Send { fn diagnostic_message(&self) -> DiagnosticMessage; /// Add diagnostic arguments by passing name and value pairs to `adder`, which are passed to /// fluent for formatting the translated diagnostic message. - fn add_args(self: Box<Self>, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue)); + fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagnosticArgName, DiagnosticArgValue)); } impl dyn MachineStopType { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 37c5bba46a7..c9e69253701 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -14,7 +14,9 @@ use crate::ty::{AdtDef, InstanceDef, UserTypeAnnotationIndex}; use crate::ty::{GenericArg, GenericArgsRef}; use rustc_data_structures::captures::Captures; -use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg}; +use rustc_errors::{ + DiagnosticArgName, DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg, +}; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind}; diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 0fe33e441f4..91b7952bec5 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -292,7 +292,7 @@ impl<O> AssertKind<O> { } } - pub fn add_args(self, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue)) + pub fn add_args(self, adder: &mut dyn FnMut(DiagnosticArgName, DiagnosticArgValue)) where O: fmt::Debug, { diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index c49f92f4f85..eba62aae60f 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -34,7 +34,7 @@ pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{ fn add_args( self: Box<Self>, - _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue), + _: &mut dyn FnMut(rustc_errors::DiagnosticArgName, rustc_errors::DiagnosticArgValue), ) {} } throw_machine_stop!(Zst) diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 49e036c3801..d5fa1108687 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -39,7 +39,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } // uses a HOF to parse anything, and <source> includes file and // `source_str`. -/// A variant of 'panictry!' that works on a `Vec<Diagnostic>` instead of a single +/// A variant of 'panictry!' that works on a `Vec<DiagnosticBuilder>` instead of a single /// `DiagnosticBuilder`. macro_rules! panictry_buffer { ($e:expr) => {{ diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index c8ec10cad17..02847a0f5f9 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -289,6 +289,9 @@ resolve_underscore_lifetime_name_cannot_be_used_here = `'_` cannot be used here .note = `'_` is a reserved lifetime name +resolve_unexpected_res_change_ty_to_const_param_sugg = + you might have meant to write a const parameter here + resolve_unreachable_label = use of unreachable label `{$name}` .label = unreachable label `{$name}` diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 50ff09feb4d..655fc9812d7 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_errors::codes::*; +use rustc_errors::{codes::*, Applicability}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{ symbol::{Ident, Symbol}, @@ -787,3 +787,16 @@ pub(crate) struct IsNotDirectlyImportable { pub(crate) span: Span, pub(crate) target: Ident, } + +#[derive(Subdiagnostic)] +#[suggestion( + resolve_unexpected_res_change_ty_to_const_param_sugg, + code = "const ", + style = "verbose" +)] +pub(crate) struct UnexpectedResChangeTyToConstParamSugg { + #[primary_span] + pub span: Span, + #[applicability] + pub applicability: Applicability, +} diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 9788fcb3c7d..1f2803d4368 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -594,9 +594,9 @@ struct DiagnosticMetadata<'ast> { /// The current trait (used to suggest). current_item: Option<&'ast Item>, - /// When processing generics and encountering a type not found, suggest introducing a type - /// param. - currently_processing_generics: bool, + /// When processing generic arguments and encountering an unresolved ident not found, + /// suggest introducing a type or const param depending on the context. + currently_processing_generic_args: bool, /// The current enclosing (non-closure) function (used for better errors). current_function: Option<(FnKind<'ast>, Span)>, @@ -1069,7 +1069,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, fn visit_generic_arg(&mut self, arg: &'ast GenericArg) { debug!("visit_generic_arg({:?})", arg); - let prev = replace(&mut self.diagnostic_metadata.currently_processing_generics, true); + let prev = replace(&mut self.diagnostic_metadata.currently_processing_generic_args, true); match arg { GenericArg::Type(ref ty) => { // We parse const arguments as path types as we cannot distinguish them during @@ -1100,7 +1100,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, }, ); - self.diagnostic_metadata.currently_processing_generics = prev; + self.diagnostic_metadata.currently_processing_generic_args = prev; return; } } @@ -1113,7 +1113,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, self.resolve_anon_const(ct, AnonConstKind::ConstArg(IsRepeatExpr::No)) } } - self.diagnostic_metadata.currently_processing_generics = prev; + self.diagnostic_metadata.currently_processing_generic_args = prev; } fn visit_assoc_constraint(&mut self, constraint: &'ast AssocConstraint) { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index a4c041f1ff2..9f810741f49 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -444,6 +444,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } self.suggest_bare_struct_literal(&mut err); + self.suggest_changing_type_to_const_param(&mut err, res, source, span); if self.suggest_pattern_match_with_let(&mut err, source, span) { // Fallback label. @@ -452,7 +453,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } self.suggest_self_or_self_ref(&mut err, path, span); - self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error); + self.detect_assoc_type_constraint_meant_as_path(&mut err, &base_error); if self.suggest_self_ty(&mut err, source, path, span) || self.suggest_self_value(&mut err, source, path, span) { @@ -491,7 +492,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { (err, candidates) } - fn detect_assoct_type_constraint_meant_as_path( + fn detect_assoc_type_constraint_meant_as_path( &self, err: &mut Diagnostic, base_error: &BaseError, @@ -799,7 +800,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { false, ) = (source, res, is_macro) { - if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { + if let Some(bounds @ [first_bound, .., last_bound]) = + self.diagnostic_metadata.current_trait_object + { fallback = true; let spans: Vec<Span> = bounds .iter() @@ -807,9 +810,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .filter(|&sp| sp != base_error.span) .collect(); - let start_span = bounds[0].span(); + let start_span = first_bound.span(); // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) - let end_span = bounds.last().unwrap().span(); + let end_span = last_bound.span(); // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) let last_bound_span = spans.last().cloned().unwrap(); let mut multi_span: MultiSpan = spans.clone().into(); @@ -1136,6 +1139,55 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } } + fn suggest_changing_type_to_const_param( + &mut self, + err: &mut Diagnostic, + res: Option<Res>, + source: PathSource<'_>, + span: Span, + ) { + let PathSource::Trait(_) = source else { return }; + + // We don't include `DefKind::Str` and `DefKind::AssocTy` as they can't be reached here anyway. + let applicability = match res { + Some(Res::PrimTy(PrimTy::Int(_) | PrimTy::Uint(_) | PrimTy::Bool | PrimTy::Char)) => { + Applicability::MachineApplicable + } + // FIXME(const_generics): Add `DefKind::TyParam` and `SelfTyParam` once we support generic + // const generics. Of course, `Struct` and `Enum` may contain ty params, too, but the + // benefits of including them here outweighs the small number of false positives. + Some(Res::Def(DefKind::Struct | DefKind::Enum, _)) + if self.r.tcx.features().adt_const_params => + { + Applicability::MaybeIncorrect + } + _ => return, + }; + + let Some(item) = self.diagnostic_metadata.current_item else { return }; + let Some(generics) = item.kind.generics() else { return }; + + let param = generics.params.iter().find_map(|param| { + // Only consider type params with exactly one trait bound. + if let [bound] = &*param.bounds + && let ast::GenericBound::Trait(tref, ast::TraitBoundModifiers::NONE) = bound + && tref.span == span + && param.ident.span.eq_ctxt(span) + { + Some(param.ident.span) + } else { + None + } + }); + + if let Some(param) = param { + err.subdiagnostic(errors::UnexpectedResChangeTyToConstParamSugg { + span: param.shrink_to_lo(), + applicability, + }); + } + } + fn suggest_pattern_match_with_let( &mut self, err: &mut Diagnostic, @@ -2419,10 +2471,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let mut iter = ident.chars().map(|c| c.is_uppercase()); let single_uppercase_char = matches!(iter.next(), Some(true)) && matches!(iter.next(), None); - if !self.diagnostic_metadata.currently_processing_generics && !single_uppercase_char { + if !self.diagnostic_metadata.currently_processing_generic_args && !single_uppercase_char { return None; } - match (self.diagnostic_metadata.current_item, single_uppercase_char, self.diagnostic_metadata.currently_processing_generics) { + match (self.diagnostic_metadata.current_item, single_uppercase_char, self.diagnostic_metadata.currently_processing_generic_args) { (Some(Item { kind: ItemKind::Fn(..), ident, .. }), _, _) if ident.name == sym::main => { // Ignore `fn main()` as we don't want to suggest `fn main<T>()` } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 1cfaa49401d..8adb0cbcc9d 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -258,7 +258,7 @@ impl ParseSess { } } - pub fn with_silent_emitter(fatal_note: Option<String>) -> Self { + 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(); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3a0ae74dd92..f6af5a4f87e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -288,19 +288,9 @@ impl Session { pub fn finish_diagnostics(&self, registry: &Registry) { self.check_miri_unleashed_features(); self.dcx().print_error_count(registry); - self.emit_future_breakage(); - } - - fn emit_future_breakage(&self) { - if !self.opts.json_future_incompat { - return; - } - - let diags = self.dcx().take_future_breakage_diagnostics(); - if diags.is_empty() { - return; + if self.opts.json_future_incompat { + self.dcx().emit_future_breakage_report(); } - self.dcx().emit_future_breakage_report(diags); } /// Returns true if the crate is a testing one. diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 5f5de57dd1d..c350a37975d 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -201,6 +201,7 @@ const X86_ALLOWED_FEATURES: &[(&str, Stability)] = &[ ("avx512dq", Unstable(sym::avx512_target_feature)), ("avx512er", Unstable(sym::avx512_target_feature)), ("avx512f", Unstable(sym::avx512_target_feature)), + ("avx512fp16", Unstable(sym::avx512_target_feature)), ("avx512ifma", Unstable(sym::avx512_target_feature)), ("avx512pf", Unstable(sym::avx512_target_feature)), ("avx512vbmi", Unstable(sym::avx512_target_feature)), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 5bab57ca56c..2dec27a6126 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2717,7 +2717,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let (trait_name, trait_verb) = if name == sym::Send { ("`Send`", "sent") } else { ("`Sync`", "shared") }; - err.clear_code(); + err.code = None; err.primary_message(format!( "{future_or_coroutine} cannot be {trait_verb} between threads safely" )); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index dee3e14f3c9..d9c5de17af8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -20,7 +20,7 @@ use crate::traits::{ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, MultiSpan, StashKey, Style, + ErrorGuaranteed, MultiSpan, StashKey, StringPart, }; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, Res}; @@ -2059,11 +2059,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()), }); err.highlighted_help(vec![ - (format!("the trait `{}` ", cand.print_trait_sugared()), Style::NoStyle), - ("is".to_string(), Style::Highlight), - (" implemented for `".to_string(), Style::NoStyle), - (cand.self_ty().to_string(), Style::Highlight), - ("`".to_string(), Style::NoStyle), + StringPart::normal(format!("the trait `{}` ", cand.print_trait_sugared())), + StringPart::highlighted("is"), + StringPart::normal(" implemented for `"), + StringPart::highlighted(cand.self_ty().to_string()), + StringPart::normal("`"), ]); if let [TypeError::Sorts(exp_found)] = &terrs[..] { @@ -2095,12 +2095,12 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { _ => (" implemented for `", ""), }; err.highlighted_help(vec![ - (format!("the trait `{}` ", cand.print_trait_sugared()), Style::NoStyle), - ("is".to_string(), Style::Highlight), - (desc.to_string(), Style::NoStyle), - (cand.self_ty().to_string(), Style::Highlight), - ("`".to_string(), Style::NoStyle), - (mention_castable.to_string(), Style::NoStyle), + StringPart::normal(format!("the trait `{}` ", cand.print_trait_sugared())), + StringPart::highlighted("is"), + StringPart::normal(desc), + StringPart::highlighted(cand.self_ty().to_string()), + StringPart::normal("`"), + StringPart::normal(mention_castable), ]); return true; } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 2af242d4b50..ead8cbe0e2f 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -200,7 +200,6 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(not(bootstrap), feature(is_val_statically_known))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index bb35b6128ea..d052dcc3e6e 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1374,59 +1374,26 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn checked_pow(self, mut exp: u32) -> Option<Self> { - // SAFETY: This path has the same behavior as the other. - if unsafe { intrinsics::is_val_statically_known(self) } - && self.unsigned_abs().is_power_of_two() - { - if self == 1 { // Avoid divide by zero - return Some(1); - } - if self == -1 { // Avoid divide by zero - return Some(if exp & 1 != 0 { -1 } else { 1 }); - } - // SAFETY: We just checked this is a power of two. and above zero. - let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 }; - if exp > Self::BITS / power_used { return None; } // Division of constants is free - - // SAFETY: exp <= Self::BITS / power_used - let res = unsafe { intrinsics::unchecked_shl( - 1 as Self, - intrinsics::unchecked_mul(power_used, exp) as Self - )}; - // LLVM doesn't always optimize out the checks - // at the ir level. - - let sign = self.is_negative() && exp & 1 != 0; - if !sign && res == Self::MIN { - None - } else if sign { - Some(res.wrapping_neg()) - } else { - Some(res) - } - } else { - if exp == 0 { - return Some(1); - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = try_opt!(acc.checked_mul(base)); - } - exp /= 2; - base = try_opt!(base.checked_mul(base)); + if exp == 0 { + return Some(1); + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = try_opt!(acc.checked_mul(base)); } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.checked_mul(base) + exp /= 2; + base = try_opt!(base.checked_mul(base)); } + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.checked_mul(base) } /// Strict exponentiation. Computes `self.pow(exp)`, panicking if @@ -2091,58 +2058,27 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn wrapping_pow(self, mut exp: u32) -> Self { - // SAFETY: This path has the same behavior as the other. - if unsafe { intrinsics::is_val_statically_known(self) } - && self.unsigned_abs().is_power_of_two() - { - if self == 1 { // Avoid divide by zero - return 1; - } - if self == -1 { // Avoid divide by zero - return if exp & 1 != 0 { -1 } else { 1 }; - } - // SAFETY: We just checked this is a power of two. and above zero. - let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 }; - if exp > Self::BITS / power_used { return 0; } // Division of constants is free - - // SAFETY: exp <= Self::BITS / power_used - let res = unsafe { intrinsics::unchecked_shl( - 1 as Self, - intrinsics::unchecked_mul(power_used, exp) as Self - )}; - // LLVM doesn't always optimize out the checks - // at the ir level. - - let sign = self.is_negative() && exp & 1 != 0; - if sign { - res.wrapping_neg() - } else { - res - } - } else { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); - } - exp /= 2; - base = base.wrapping_mul(base); - } + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc: Self = 1; - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + } + exp /= 2; + base = base.wrapping_mul(base); } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.wrapping_mul(base) } /// Calculates `self` + `rhs` @@ -2625,68 +2561,36 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { - // SAFETY: This path has the same behavior as the other. - if unsafe { intrinsics::is_val_statically_known(self) } - && self.unsigned_abs().is_power_of_two() - { - if self == 1 { // Avoid divide by zero - return (1, false); - } - if self == -1 { // Avoid divide by zero - return (if exp & 1 != 0 { -1 } else { 1 }, false); - } - // SAFETY: We just checked this is a power of two. and above zero. - let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 }; - if exp > Self::BITS / power_used { return (0, true); } // Division of constants is free - - // SAFETY: exp <= Self::BITS / power_used - let res = unsafe { intrinsics::unchecked_shl( - 1 as Self, - intrinsics::unchecked_mul(power_used, exp) as Self - )}; - // LLVM doesn't always optimize out the checks - // at the ir level. - - let sign = self.is_negative() && exp & 1 != 0; - let overflow = res == Self::MIN; - if sign { - (res.wrapping_neg(), overflow) - } else { - (res, overflow) - } - } else { - if exp == 0 { - return (1,false); - } - let mut base = self; - let mut acc: Self = 1; - let mut overflown = false; - // Scratch space for storing results of overflowing_mul. - let mut r; - - while exp > 1 { - if (exp & 1) == 1 { - r = acc.overflowing_mul(base); - acc = r.0; - overflown |= r.1; - } - exp /= 2; - r = base.overflowing_mul(base); - base = r.0; + if exp == 0 { + return (1,false); + } + let mut base = self; + let mut acc: Self = 1; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; + + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; overflown |= r.1; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; - r + exp /= 2; + r = base.overflowing_mul(base); + base = r.0; + overflown |= r.1; } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + r = acc.overflowing_mul(base); + r.1 |= overflown; + r } /// Raises self to the power of `exp`, using exponentiation by squaring. @@ -2704,68 +2608,28 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] #[rustc_inherit_overflow_checks] - #[track_caller] // Hides the hackish overflow check for powers of two. pub const fn pow(self, mut exp: u32) -> Self { - // SAFETY: This path has the same behavior as the other. - if unsafe { intrinsics::is_val_statically_known(self) } - && self.unsigned_abs().is_power_of_two() - { - if self == 1 { // Avoid divide by zero - return 1; - } - if self == -1 { // Avoid divide by zero - return if exp & 1 != 0 { -1 } else { 1 }; - } - // SAFETY: We just checked this is a power of two. and above zero. - let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 }; - if exp > Self::BITS / power_used { // Division of constants is free - #[allow(arithmetic_overflow)] - return Self::MAX * Self::MAX * 0; - } + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc = 1; - // SAFETY: exp <= Self::BITS / power_used - let res = unsafe { intrinsics::unchecked_shl( - 1 as Self, - intrinsics::unchecked_mul(power_used, exp) as Self - )}; - // LLVM doesn't always optimize out the checks - // at the ir level. - - let sign = self.is_negative() && exp & 1 != 0; - #[allow(arithmetic_overflow)] - if !sign && res == Self::MIN { - // So it panics. - _ = Self::MAX * Self::MAX; - } - if sign { - res.wrapping_neg() - } else { - res - } - } else { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc * base; - } - exp /= 2; - base = base * base; + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * base; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc * base + exp /= 2; + base = base * base; } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base } /// Returns the square root of the number, rounded down. diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index d450c68a5b2..a217c2e259d 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1364,49 +1364,28 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn checked_pow(self, mut exp: u32) -> Option<Self> { - // SAFETY: This path has the same behavior as the other. - if unsafe { intrinsics::is_val_statically_known(self) } - && self.is_power_of_two() - { - if self == 1 { // Avoid divide by zero - return Some(1); - } - // SAFETY: We just checked this is a power of two. and above zero. - let power_used = unsafe { intrinsics::cttz_nonzero(self) as u32 }; - if exp > Self::BITS / power_used { return None; } // Division of constants is free - - // SAFETY: exp <= Self::BITS / power_used - unsafe { Some(intrinsics::unchecked_shl( - 1 as Self, - intrinsics::unchecked_mul(power_used, exp) as Self - )) } - // LLVM doesn't always optimize out the checks - // at the ir level. - } else { - if exp == 0 { - return Some(1); - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = try_opt!(acc.checked_mul(base)); - } - exp /= 2; - base = try_opt!(base.checked_mul(base)); + if exp == 0 { + return Some(1); + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = try_opt!(acc.checked_mul(base)); } + exp /= 2; + base = try_opt!(base.checked_mul(base)); + } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. - acc.checked_mul(base) - } + acc.checked_mul(base) } /// Strict exponentiation. Computes `self.pow(exp)`, panicking if @@ -1908,48 +1887,27 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn wrapping_pow(self, mut exp: u32) -> Self { - // SAFETY: This path has the same behavior as the other. - if unsafe { intrinsics::is_val_statically_known(self) } - && self.is_power_of_two() - { - if self == 1 { // Avoid divide by zero - return 1; - } - // SAFETY: We just checked this is a power of two. and above zero. - let power_used = unsafe { intrinsics::cttz_nonzero(self) as u32 }; - if exp > Self::BITS / power_used { return 0; } // Division of constants is free - - // SAFETY: exp <= Self::BITS / power_used - unsafe { intrinsics::unchecked_shl( - 1 as Self, - intrinsics::unchecked_mul(power_used, exp) as Self - )} - // LLVM doesn't always optimize out the checks - // at the ir level. - } else { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); - } - exp /= 2; - base = base.wrapping_mul(base); - } + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc: Self = 1; - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + } + exp /= 2; + base = base.wrapping_mul(base); } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.wrapping_mul(base) } /// Calculates `self` + `rhs` @@ -2383,58 +2341,37 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { - // SAFETY: This path has the same behavior as the other. - if unsafe { intrinsics::is_val_statically_known(self) } - && self.is_power_of_two() - { - if self == 1 { // Avoid divide by zero - return (1, false); - } - // SAFETY: We just checked this is a power of two. and above zero. - let power_used = unsafe { intrinsics::cttz_nonzero(self) as u32 }; - if exp > Self::BITS / power_used { return (0, true); } // Division of constants is free - - // SAFETY: exp <= Self::BITS / power_used - unsafe { (intrinsics::unchecked_shl( - 1 as Self, - intrinsics::unchecked_mul(power_used, exp) as Self - ), false) } - // LLVM doesn't always optimize out the checks - // at the ir level. - } else { - if exp == 0{ - return (1,false); - } - let mut base = self; - let mut acc: Self = 1; - let mut overflown = false; - // Scratch space for storing results of overflowing_mul. - let mut r; - - while exp > 1 { - if (exp & 1) == 1 { - r = acc.overflowing_mul(base); - acc = r.0; - overflown |= r.1; - } - exp /= 2; - r = base.overflowing_mul(base); - base = r.0; + if exp == 0{ + return (1,false); + } + let mut base = self; + let mut acc: Self = 1; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; + + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; overflown |= r.1; } + exp /= 2; + r = base.overflowing_mul(base); + base = r.0; + overflown |= r.1; + } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + r = acc.overflowing_mul(base); + r.1 |= overflown; - r - } + r } /// Raises self to the power of `exp`, using exponentiation by squaring. @@ -2450,64 +2387,28 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] #[rustc_inherit_overflow_checks] - #[track_caller] // Hides the hackish overflow check for powers of two. pub const fn pow(self, mut exp: u32) -> Self { - // LLVM now knows that `self` is a constant value, but not a - // constant in Rust. This allows us to compute the power used at - // compile-time. - // - // This will likely add a branch in debug builds, but this should - // be ok. - // - // This is a massive performance boost in release builds as you can - // get the power of a power of two and the exponent through a `shl` - // instruction, but we must add a couple more checks for parity with - // our own `pow`. - // SAFETY: This path has the same behavior as the other. - if unsafe { intrinsics::is_val_statically_known(self) } - && self.is_power_of_two() - { - if self == 1 { // Avoid divide by zero - return 1; - } - // SAFETY: We just checked this is a power of two. and above zero. - let power_used = unsafe { intrinsics::cttz_nonzero(self) as u32 }; - if exp > Self::BITS / power_used { // Division of constants is free - #[allow(arithmetic_overflow)] - return Self::MAX * Self::MAX * 0; - } + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc = 1; - // SAFETY: exp <= Self::BITS / power_used - unsafe { intrinsics::unchecked_shl( - 1 as Self, - intrinsics::unchecked_mul(power_used, exp) as Self - )} - // LLVM doesn't always optimize out the checks - // at the ir level. - } else { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc * base; - } - exp /= 2; - base = base * base; + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * base; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc * base + exp /= 2; + base = base * base; } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base } /// Returns the square root of the number, rounded down. diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index a743d1c870a..7513abf17c8 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -166,7 +166,7 @@ checksum = "5676cea088c32290fe65c82895be9d06dd21e0fa49bb97ca840529e9417ab71a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", "synstructure", ] @@ -313,6 +313,17 @@ dependencies = [ ] [[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] name = "derive_arbitrary" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -320,7 +331,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -582,6 +593,7 @@ dependencies = [ "project-model", "ra-ap-rustc_abi", "ra-ap-rustc_index", + "ra-ap-rustc_pattern_analysis", "rustc-hash", "scoped-tls", "smallvec", @@ -1407,20 +1419,20 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.21.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7816f980fab89e878ff2e916e2077d484e3aa1c619a3cc982c8a417c3dfe45fa" +checksum = "8ce9100fc66c6c60aeeb076868ead9c2eaa65d6a5a90404f08c242327a92ff4b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "ra-ap-rustc_index", "tracing", ] [[package]] name = "ra-ap-rustc_index" -version = "0.21.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8352918d61aa4afab9f2ed7314cf638976b20949b3d61d2f468c975b0d251f24" +checksum = "5e5313d7f243b63ef9e58d94355b11aa8499f1328055f1f58adf0a5ea7d2faca" dependencies = [ "arrayvec", "ra-ap-rustc_index_macros", @@ -1429,21 +1441,21 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.21.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a9424018828155a3e3596515598f90e68427d8f35eff6df7f0856c73fc58a8" +checksum = "a83108ebf3e73dde205b9c25706209bcd7736480820f90ded28eabaf8b469f25" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", "synstructure", ] [[package]] name = "ra-ap-rustc_lexer" -version = "0.21.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc741c7a78103efab416b562e35bd73c8d4967478575010c86c6062f8d3cbf29" +checksum = "d2d221356e5717595e8a0afa5fba1620dcb4032ab784dc4d98fdc7284e3feb66" dependencies = [ "unicode-properties", "unicode-xid", @@ -1451,15 +1463,29 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.21.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d557201d71792487bd2bab637ab5be9aa6fff59b88e25e12de180b0f9d2df60f" +checksum = "ab62fc925612374103b4f178da347b535b35d9eb1ff5ba42105c990b2e25a164" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", ] [[package]] +name = "ra-ap-rustc_pattern_analysis" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c4085e0c771fd4b883930b599ef42966b855762bbe4052c17673b3253421a6d" +dependencies = [ + "derivative", + "ra-ap-rustc_index", + "rustc-hash", + "rustc_apfloat", + "smallvec", + "tracing", +] + +[[package]] name = "rayon" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1593,7 +1619,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1609,6 +1635,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] +name = "rustc_apfloat" +version = "0.2.0+llvm-462a31f5a5ab" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465187772033a5ee566f69fe008df03628fce549a0899aae76f0a0c2e34696be" +dependencies = [ + "bitflags 1.3.2", + "smallvec", +] + +[[package]] name = "ryu" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1670,7 +1706,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1693,7 +1729,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1707,9 +1743,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" [[package]] name = "smol_str" @@ -1772,6 +1808,17 @@ dependencies = [ [[package]] name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" @@ -1789,7 +1836,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", "unicode-xid", ] @@ -1876,7 +1923,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1977,7 +2024,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 2547f1ccb99..56db5a28c08 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -79,10 +79,11 @@ tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.21.0", default-features = false } -ra-ap-rustc_parse_format = { version = "0.21.0", default-features = false } -ra-ap-rustc_index = { version = "0.21.0", default-features = false } -ra-ap-rustc_abi = { version = "0.21.0", default-features = false } +ra-ap-rustc_lexer = { version = "0.33.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.33.0", default-features = false } +ra-ap-rustc_index = { version = "0.33.0", default-features = false } +ra-ap-rustc_abi = { version = "0.33.0", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.33.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. sourcegen = { path = "./crates/sourcegen" } diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 92d2b9c3f57..90da7efd4a8 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -2,8 +2,8 @@ #![warn(rust_2018_idioms, unused_lifetimes)] -mod input; mod change; +mod input; use std::panic; diff --git a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs index fb7505ba2dd..4be6ae7481d 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs @@ -18,28 +18,6 @@ pub enum CfgAtom { KeyValue { key: SmolStr, value: SmolStr }, } -impl CfgAtom { - /// Returns `true` when the atom comes from the target specification. - /// - /// If this returns `true`, then changing this atom requires changing the compilation target. If - /// it returns `false`, the atom might come from a build script or the build system. - pub fn is_target_defined(&self) -> bool { - match self { - CfgAtom::Flag(flag) => matches!(&**flag, "unix" | "windows"), - CfgAtom::KeyValue { key, value: _ } => matches!( - &**key, - "target_arch" - | "target_os" - | "target_env" - | "target_family" - | "target_endian" - | "target_pointer_width" - | "target_vendor" // NOTE: `target_feature` is left out since it can be configured via `-Ctarget-feature` - ), - } - } -} - impl fmt::Display for CfgAtom { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 6b178e7b04a..454d6fc5384 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -131,11 +131,9 @@ impl CfgDiff { /// of both. pub fn new(enable: Vec<CfgAtom>, disable: Vec<CfgAtom>) -> Option<CfgDiff> { let mut occupied = FxHashSet::default(); - for item in enable.iter().chain(disable.iter()) { - if !occupied.insert(item) { - // was present - return None; - } + if enable.iter().chain(disable.iter()).any(|item| !occupied.insert(item)) { + // was present + return None; } Some(CfgDiff { enable, disable }) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 8fbfcc81d28..a8e081705e3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -32,6 +32,7 @@ use crate::{ VariantId, }; +/// Desugared attributes of an item post `cfg_attr` expansion. #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct Attrs(RawAttrs); @@ -228,7 +229,6 @@ pub enum DocAtom { KeyValue { key: SmolStr, value: SmolStr }, } -// Adapted from `CfgExpr` parsing code #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum DocExpr { Invalid, @@ -448,10 +448,7 @@ impl AttrsWithOwner { let map = db.fields_attrs_source_map(id.parent); let file_id = id.parent.file_id(db); let root = db.parse_or_expand(file_id); - let owner = match &map[id.local_id] { - Either::Left(it) => ast::AnyHasAttrs::new(it.to_node(&root)), - Either::Right(it) => ast::AnyHasAttrs::new(it.to_node(&root)), - }; + let owner = ast::AnyHasAttrs::new(map[id.local_id].to_node(&root)); InFile::new(file_id, owner) } AttrDefId::AdtId(adt) => match adt { @@ -634,7 +631,7 @@ fn attrs_from_item_tree_assoc<'db, N: ItemTreeModItemNode>( pub(crate) fn fields_attrs_source_map( db: &dyn DefDatabase, def: VariantId, -) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>> { +) -> Arc<ArenaMap<LocalFieldId, AstPtr<Either<ast::TupleField, ast::RecordField>>>> { let mut res = ArenaMap::default(); let child_source = def.child_source(db); @@ -643,7 +640,7 @@ pub(crate) fn fields_attrs_source_map( idx, variant .as_ref() - .either(|l| Either::Left(AstPtr::new(l)), |r| Either::Right(AstPtr::new(r))), + .either(|l| AstPtr::new(l).wrap_left(), |r| AstPtr::new(r).wrap_right()), ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index 81132d73853..e4308c6b7f1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -1,10 +1,10 @@ //! Defines `Body`: a lowered representation of bodies of functions, statics and //! consts. mod lower; +mod pretty; +pub mod scope; #[cfg(test)] mod tests; -pub mod scope; -mod pretty; use std::ops::Index; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 5fc4867bfa6..492ea6d5c59 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -1335,6 +1335,7 @@ impl ExprCollector<'_> { let args = record_pat_field_list .fields() .filter_map(|f| { + self.check_cfg(&f)?; let ast_pat = f.pat()?; let pat = self.collect_pat(ast_pat, binding_list); let name = f.field_name()?.as_name(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index c9789ceb207..708abb53693 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -194,7 +194,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba fn fields_attrs_source_map( &self, def: VariantId, - ) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>>; + ) -> Arc<ArenaMap<LocalFieldId, AstPtr<Either<ast::TupleField, ast::RecordField>>>>; #[salsa::invoke(AttrsWithOwner::attrs_query)] fn attrs(&self, def: AttrDefId) -> Attrs; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 1a33868a78c..ac44d379415 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -12,8 +12,8 @@ //! //! See also a neighboring `body` module. -pub mod type_ref; pub mod format_args; +pub mod type_ref; use std::fmt; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index adf070fe7da..243de663977 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -25,13 +25,13 @@ extern crate ra_ap_rustc_abi as rustc_abi; pub mod db; pub mod attr; -pub mod path; pub mod builtin_type; -pub mod per_ns; pub mod item_scope; +pub mod path; +pub mod per_ns; -pub mod lower; pub mod expander; +pub mod lower; pub mod dyn_map; @@ -46,24 +46,24 @@ pub use self::hir::type_ref; pub mod body; pub mod resolver; -mod trace; pub mod nameres; +mod trace; -pub mod src; pub mod child_by_source; +pub mod src; -pub mod visibility; pub mod find_path; pub mod import_map; +pub mod visibility; pub use rustc_abi as layout; use triomphe::Arc; #[cfg(test)] -mod test_db; -#[cfg(test)] mod macro_expansion_tests; mod pretty; +#[cfg(test)] +mod test_db; use std::{ hash::{Hash, Hasher}, @@ -73,7 +73,6 @@ use std::{ use base_db::{impl_intern_key, salsa, CrateId, Edition}; use hir_expand::{ ast_id_map::{AstIdNode, FileAstId}, - attrs::{Attr, AttrId, AttrInput}, builtin_attr_macro::BuiltinAttrExpander, builtin_derive_macro::BuiltinDeriveExpander, builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander}, @@ -939,6 +938,15 @@ impl From<AssocItemId> for AttrDefId { } } } +impl From<VariantId> for AttrDefId { + fn from(vid: VariantId) -> Self { + match vid { + VariantId::EnumVariantId(id) => id.into(), + VariantId::StructId(id) => id.into(), + VariantId::UnionId(id) => id.into(), + } + } +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum VariantId { @@ -1265,60 +1273,6 @@ fn macro_call_as_call_id_with_eager( Ok(res) } -fn derive_macro_as_call_id( - db: &dyn DefDatabase, - item_attr: &AstIdWithPath<ast::Adt>, - derive_attr_index: AttrId, - derive_pos: u32, - call_site: Span, - krate: CrateId, - resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, -) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { - let (macro_id, def_id) = resolver(item_attr.path.clone()) - .filter(|(_, def_id)| def_id.is_derive()) - .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; - let call_id = def_id.as_lazy_macro( - db.upcast(), - krate, - MacroCallKind::Derive { - ast_id: item_attr.ast_id, - derive_index: derive_pos, - derive_attr_index, - }, - call_site, - ); - Ok((macro_id, def_id, call_id)) -} - -fn attr_macro_as_call_id( - db: &dyn DefDatabase, - item_attr: &AstIdWithPath<ast::Item>, - macro_attr: &Attr, - krate: CrateId, - def: MacroDefId, -) -> MacroCallId { - let arg = match macro_attr.input.as_deref() { - Some(AttrInput::TokenTree(tt)) => { - let mut tt = tt.as_ref().clone(); - tt.delimiter = tt::Delimiter::invisible_spanned(macro_attr.span); - Some(tt) - } - - _ => None, - }; - - def.as_lazy_macro( - db.upcast(), - krate, - MacroCallKind::Attr { - ast_id: item_attr.ast_id, - attr_args: arg.map(Arc::new), - invoc_attr_index: macro_attr.id, - }, - macro_attr.span, - ) -} - #[derive(Debug)] pub struct UnresolvedMacro { pub path: hir_expand::mod_path::ModPath, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index 6d365bd93c0..d0ae1f59f7c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -1,11 +1,11 @@ //! Tests specific to declarative macros, aka macros by example. This covers //! both stable `macro_rules!` macros as well as unstable `macro` macros. -mod tt_conversion; mod matching; mod meta_syntax; mod metavar_expr; mod regression; +mod tt_conversion; use expect_test::expect; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 550ce35f127..ec299405387 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -9,9 +9,9 @@ //! write unit-tests (in fact, we used to do that), but that makes tests brittle //! and harder to understand. -mod mbe; -mod builtin_fn_macro; mod builtin_derive_macro; +mod builtin_fn_macro; +mod mbe; mod proc_macros; use std::{iter, ops::Range, sync}; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 7eb2f3adddb..2295df16fdc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -48,11 +48,11 @@ //! the result pub mod attr_resolution; -pub mod proc_macro; -pub mod diagnostics; mod collector; +pub mod diagnostics; mod mod_resolution; mod path_resolution; +pub mod proc_macro; #[cfg(test)] mod tests; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index 6288b8366bf..1cadae8c87c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -1,16 +1,21 @@ //! Post-nameres attribute resolution. -use hir_expand::{attrs::Attr, MacroCallId}; +use base_db::CrateId; +use hir_expand::{ + attrs::{Attr, AttrId, AttrInput}, + MacroCallId, MacroCallKind, MacroDefId, +}; +use span::Span; use syntax::{ast, SmolStr}; +use triomphe::Arc; use crate::{ attr::builtin::{find_builtin_attr_idx, TOOL_MODULES}, - attr_macro_as_call_id, db::DefDatabase, item_scope::BuiltinShadowMode, nameres::path_resolution::ResolveMode, - path::{ModPath, PathKind}, - AstIdWithPath, LocalModuleId, UnresolvedMacro, + path::{self, ModPath, PathKind}, + AstIdWithPath, LocalModuleId, MacroId, UnresolvedMacro, }; use super::{DefMap, MacroSubNs}; @@ -93,3 +98,57 @@ impl DefMap { false } } + +pub(super) fn attr_macro_as_call_id( + db: &dyn DefDatabase, + item_attr: &AstIdWithPath<ast::Item>, + macro_attr: &Attr, + krate: CrateId, + def: MacroDefId, +) -> MacroCallId { + let arg = match macro_attr.input.as_deref() { + Some(AttrInput::TokenTree(tt)) => { + let mut tt = tt.as_ref().clone(); + tt.delimiter = tt::Delimiter::invisible_spanned(macro_attr.span); + Some(tt) + } + + _ => None, + }; + + def.as_lazy_macro( + db.upcast(), + krate, + MacroCallKind::Attr { + ast_id: item_attr.ast_id, + attr_args: arg.map(Arc::new), + invoc_attr_index: macro_attr.id, + }, + macro_attr.span, + ) +} + +pub(super) fn derive_macro_as_call_id( + db: &dyn DefDatabase, + item_attr: &AstIdWithPath<ast::Adt>, + derive_attr_index: AttrId, + derive_pos: u32, + call_site: Span, + krate: CrateId, + resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, +) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { + let (macro_id, def_id) = resolver(item_attr.path.clone()) + .filter(|(_, def_id)| def_id.is_derive()) + .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; + let call_id = def_id.as_lazy_macro( + db.upcast(), + krate, + MacroCallKind::Derive { + ast_id: item_attr.ast_id, + derive_index: derive_pos, + derive_attr_index, + }, + call_site, + ); + Ok((macro_id, def_id, call_id)) +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 1c0f4d4d35f..248d3213d5d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -30,9 +30,7 @@ use triomphe::Arc; use crate::{ attr::Attrs, - attr_macro_as_call_id, db::DefDatabase, - derive_macro_as_call_id, item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, item_tree::{ self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, @@ -40,6 +38,7 @@ use crate::{ }, macro_call_as_call_id, macro_call_as_call_id_with_eager, nameres::{ + attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id}, diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, @@ -1245,7 +1244,9 @@ impl DefCollector<'_> { MacroDefId { kind: MacroDefKind::BuiltInAttr(expander, _),.. } if expander.is_derive() ) { - // Resolved to `#[derive]` + // Resolved to `#[derive]`, we don't actually expand this attribute like + // normal (as that would just be an identity expansion with extra output) + // Instead we treat derive attributes special and apply them separately. let item_tree = tree.item_tree(self.db); let ast_adt_id: FileAstId<ast::Adt> = match *mod_item { @@ -1284,7 +1285,8 @@ impl DefCollector<'_> { } // We treat the #[derive] macro as an attribute call, but we do not resolve it for nameres collection. - // This is just a trick to be able to resolve the input to derives as proper paths. + // This is just a trick to be able to resolve the input to derives + // as proper paths in `Semantics`. // Check the comment in [`builtin_attr_macro`]. let call_id = attr_macro_as_call_id( self.db, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs index 7bdd6db9321..530f10a0684 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs @@ -155,7 +155,14 @@ impl PartialEq for AstIdMap { impl Eq for AstIdMap {} impl AstIdMap { - pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap { + pub(crate) fn ast_id_map( + db: &dyn ExpandDatabase, + file_id: span::HirFileId, + ) -> triomphe::Arc<AstIdMap> { + triomphe::Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id))) + } + + fn from_source(node: &SyntaxNode) -> AstIdMap { assert!(node.parent().is_none()); let mut res = AstIdMap::default(); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 67a318afd06..30d38299d99 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -117,14 +117,10 @@ impl RawAttrs { None => return smallvec![attr.clone()], }; let index = attr.id; - let attrs = - parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(|(idx, attr)| { - let tree = Subtree { - delimiter: tt::Delimiter::invisible_spanned(attr.first()?.first_span()), - token_trees: attr.to_vec(), - }; - Attr::from_tt(db, &tree, index.with_cfg_attr(idx)) - }); + let attrs = parts + .enumerate() + .take(1 << AttrId::CFG_ATTR_BITS) + .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx))); let cfg_options = &crate_graph[krate].cfg_options; let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; @@ -222,12 +218,40 @@ impl Attr { Some(Attr { id, path, input, span }) } - fn from_tt(db: &dyn ExpandDatabase, tt: &tt::Subtree, id: AttrId) -> Option<Attr> { - // FIXME: Unecessary roundtrip tt -> ast -> tt - let (parse, map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MetaItem); - let ast = ast::Meta::cast(parse.syntax_node())?; - - Self::from_src(db, ast, SpanMapRef::ExpansionSpanMap(&map), id) + fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree], id: AttrId) -> Option<Attr> { + let span = tt.first()?.first_span(); + let path_end = tt + .iter() + .position(|tt| { + !matches!( + tt, + tt::TokenTree::Leaf( + tt::Leaf::Punct(tt::Punct { char: ':' | '$', .. }) | tt::Leaf::Ident(_), + ) + ) + }) + .unwrap_or_else(|| tt.len()); + + let (path, input) = tt.split_at(path_end); + let path = Interned::new(ModPath::from_tt(db, path)?); + + let input = match input.get(0) { + Some(tt::TokenTree::Subtree(tree)) => { + Some(Interned::new(AttrInput::TokenTree(Box::new(tree.clone())))) + } + Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => { + let input = match input.get(1) { + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text, .. }))) => { + //FIXME the trimming here isn't quite right, raw strings are not handled + Some(Interned::new(AttrInput::Literal(text.trim_matches('"').into()))) + } + _ => None, + }; + input + } + _ => None, + }; + Some(Attr { id, path, input, span }) } pub fn path(&self) -> &ModPath { @@ -277,29 +301,8 @@ impl Attr { .token_trees .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))) .filter_map(move |tts| { - if tts.is_empty() { - return None; - } - // FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation - // here or maybe just parse a mod path from a token tree directly - let subtree = tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(tts.first()?.first_span()), - token_trees: tts.to_vec(), - }; - let (parse, span_map) = - mbe::token_tree_to_syntax_node(&subtree, mbe::TopEntryPoint::MetaItem); - let meta = ast::Meta::cast(parse.syntax_node())?; - // Only simple paths are allowed. - if meta.eq_token().is_some() || meta.expr().is_some() || meta.token_tree().is_some() - { - return None; - } - let path = meta.path()?; - let call_site = span_map.span_at(path.syntax().text_range().start()); - Some(( - ModPath::from_src(db, path, SpanMapRef::ExpansionSpanMap(&span_map))?, - call_site, - )) + let span = tts.first()?.first_span(); + Some((ModPath::from_tt(db, tts)?, span)) }); Some(paths) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs index 55157abe671..dd2aa94ad01 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs @@ -48,11 +48,13 @@ impl BuiltinAttrExpander { register_builtin! { expand: (bench, Bench) => dummy_attr_expand, + (cfg, Cfg) => dummy_attr_expand, + (cfg_attr, CfgAttr) => dummy_attr_expand, (cfg_accessible, CfgAccessible) => dummy_attr_expand, (cfg_eval, CfgEval) => dummy_attr_expand, - (derive, Derive) => derive_attr_expand, + (derive, Derive) => derive_expand, // derive const is equivalent to derive for our proposes. - (derive_const, DeriveConst) => derive_attr_expand, + (derive_const, DeriveConst) => derive_expand, (global_allocator, GlobalAllocator) => dummy_attr_expand, (test, Test) => dummy_attr_expand, (test_case, TestCase) => dummy_attr_expand @@ -91,7 +93,7 @@ fn dummy_attr_expand( /// always resolve as a derive without nameres recollecting them. /// So this hacky approach is a lot more friendly for us, though it does require a bit of support in /// [`hir::Semantics`] to make this work. -fn derive_attr_expand( +fn derive_expand( db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 2f8c0951b14..8c43017971f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -1,16 +1,11 @@ //! Defines database & queries for macro expansion. -use std::sync::OnceLock; - -use base_db::{ - salsa::{self, debug::DebugQueryTable}, - CrateId, Edition, FileId, SourceDatabase, VersionReq, -}; +use base_db::{salsa, CrateId, FileId, SourceDatabase}; use either::Either; use limit::Limit; use mbe::{syntax_node_to_token_tree, ValueResult}; use rustc_hash::FxHashSet; -use span::{Span, SyntaxContextId}; +use span::SyntaxContextId; use syntax::{ ast::{self, HasAttrs}, AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, @@ -19,13 +14,14 @@ use triomphe::Arc; use crate::{ ast_id_map::AstIdMap, - attrs::{collect_attrs, RawAttrs}, + attrs::collect_attrs, builtin_attr_macro::pseudo_derive_attr_expansion, builtin_fn_macro::EagerExpander, + declarative::DeclarativeMacroExpander, fixup::{self, reverse_fixups, SyntaxFixupUndoInfo}, hygiene::{ - apply_mark, span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt, - SyntaxContextData, Transparency, + span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt, + SyntaxContextData, }, proc_macro::ProcMacros, span_map::{RealSpanMap, SpanMap, SpanMapRef}, @@ -44,82 +40,6 @@ use crate::{ static TOKEN_LIMIT: Limit = Limit::new(1_048_576); #[derive(Debug, Clone, Eq, PartialEq)] -/// Old-style `macro_rules` or the new macros 2.0 -pub struct DeclarativeMacroExpander { - pub mac: mbe::DeclarativeMacro<span::Span>, - pub transparency: Transparency, -} - -// FIXME: Remove this once we drop support for 1.76 -static REQUIREMENT: OnceLock<VersionReq> = OnceLock::new(); - -impl DeclarativeMacroExpander { - pub fn expand( - &self, - db: &dyn ExpandDatabase, - tt: tt::Subtree, - call_id: MacroCallId, - ) -> ExpandResult<tt::Subtree> { - let loc = db.lookup_intern_macro_call(call_id); - let toolchain = &db.crate_graph()[loc.def.krate].toolchain; - let new_meta_vars = toolchain.as_ref().map_or(false, |version| { - REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( - &base_db::Version { - pre: base_db::Prerelease::EMPTY, - build: base_db::BuildMetadata::EMPTY, - major: version.major, - minor: version.minor, - patch: version.patch, - }, - ) - }); - match self.mac.err() { - Some(e) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: loc.call_site, close: loc.call_site }), - ExpandError::other(format!("invalid macro definition: {e}")), - ), - None => self - .mac - .expand( - &tt, - |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency), - new_meta_vars, - loc.call_site, - ) - .map_err(Into::into), - } - } - - pub fn expand_unhygienic( - &self, - db: &dyn ExpandDatabase, - tt: tt::Subtree, - krate: CrateId, - call_site: Span, - ) -> ExpandResult<tt::Subtree> { - let toolchain = &db.crate_graph()[krate].toolchain; - let new_meta_vars = toolchain.as_ref().map_or(false, |version| { - REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( - &base_db::Version { - pre: base_db::Prerelease::EMPTY, - build: base_db::BuildMetadata::EMPTY, - major: version.major, - minor: version.minor, - patch: version.patch, - }, - ) - }); - match self.mac.err() { - Some(e) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), - ExpandError::other(format!("invalid macro definition: {e}")), - ), - None => self.mac.expand(&tt, |_| (), new_meta_vars, call_site).map_err(Into::into), - } - } -} - -#[derive(Debug, Clone, Eq, PartialEq)] pub enum TokenExpander { /// Old-style `macro_rules` or the new macros 2.0 DeclarativeMacro(Arc<DeclarativeMacroExpander>), @@ -141,6 +61,7 @@ pub trait ExpandDatabase: SourceDatabase { #[salsa::input] fn proc_macros(&self) -> Arc<ProcMacros>; + #[salsa::invoke(AstIdMap::ast_id_map)] fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; /// Main public API -- parses a hir file, not caring whether it's a real @@ -156,8 +77,10 @@ pub trait ExpandDatabase: SourceDatabase { macro_file: MacroFileId, ) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>; #[salsa::transparent] + #[salsa::invoke(SpanMap::new)] fn span_map(&self, file_id: HirFileId) -> SpanMap; + #[salsa::invoke(crate::span_map::real_span_map)] fn real_span_map(&self, file_id: FileId) -> Arc<RealSpanMap>; /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the @@ -173,6 +96,7 @@ pub trait ExpandDatabase: SourceDatabase { #[salsa::transparent] fn setup_syntax_context_root(&self) -> (); #[salsa::transparent] + #[salsa::invoke(crate::hygiene::dump_syntax_contexts)] fn dump_syntax_contexts(&self) -> String; /// Lowers syntactic macro call to a token tree representation. That's a firewall @@ -184,8 +108,10 @@ pub trait ExpandDatabase: SourceDatabase { ) -> ValueResult<Option<(Arc<tt::Subtree>, SyntaxFixupUndoInfo)>, Arc<Box<[SyntaxError]>>>; /// Fetches the expander for this macro. #[salsa::transparent] + #[salsa::invoke(TokenExpander::macro_expander)] fn macro_expander(&self, id: MacroDefId) -> TokenExpander; /// Fetches (and compiles) the expander of this decl macro. + #[salsa::invoke(DeclarativeMacroExpander::expander)] fn decl_macro_expander( &self, def_crate: CrateId, @@ -203,36 +129,6 @@ pub trait ExpandDatabase: SourceDatabase { ) -> ExpandResult<Box<[SyntaxError]>>; } -#[inline] -pub fn span_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap { - match file_id.repr() { - HirFileIdRepr::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)), - HirFileIdRepr::MacroFile(m) => { - SpanMap::ExpansionSpanMap(db.parse_macro_expansion(m).value.1) - } - } -} - -pub fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc<RealSpanMap> { - use syntax::ast::HasModuleItem; - let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)]; - let ast_id_map = db.ast_id_map(file_id.into()); - let tree = db.parse(file_id).tree(); - // FIXME: Descend into modules and other item containing items that are not annotated with attributes - // and allocate pairs for those as well. This gives us finer grained span anchors resulting in - // better incrementality - pairs.extend( - tree.items() - .map(|item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase())), - ); - - Arc::new(RealSpanMap::from_file( - file_id, - pairs.into_boxed_slice(), - tree.syntax().text_range().end(), - )) -} - /// This expands the given macro call, but with different arguments. This is /// used for completion, where we want to see what 'would happen' if we insert a /// token. The `token_to_map` mapped down into the expansion, with the mapped @@ -357,10 +253,6 @@ pub fn expand_speculative( Some((node.syntax_node(), token)) } -fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<AstIdMap> { - Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id))) -} - fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode { match file_id.repr() { HirFileIdRepr::FileId(file_id) => db.parse(file_id).syntax_node(), @@ -412,7 +304,10 @@ fn parse_macro_expansion_error( .map(|it| it.0.errors().to_vec().into_boxed_slice()) } -fn parse_with_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> (Parse<SyntaxNode>, SpanMap) { +pub(crate) fn parse_with_map( + db: &dyn ExpandDatabase, + file_id: HirFileId, +) -> (Parse<SyntaxNode>, SpanMap) { match file_id.repr() { HirFileIdRepr::FileId(file_id) => { (db.parse(file_id).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id))) @@ -581,100 +476,18 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy .unwrap_or_default() } -fn decl_macro_expander( - db: &dyn ExpandDatabase, - def_crate: CrateId, - id: AstId<ast::Macro>, -) -> Arc<DeclarativeMacroExpander> { - let crate_data = &db.crate_graph()[def_crate]; - let is_2021 = crate_data.edition >= Edition::Edition2021; - let (root, map) = parse_with_map(db, id.file_id); - let root = root.syntax_node(); - - let transparency = |node| { - // ... would be nice to have the item tree here - let attrs = RawAttrs::new(db, node, map.as_ref()).filter(db, def_crate); - match &*attrs - .iter() - .find(|it| { - it.path.as_ident().and_then(|it| it.as_str()) == Some("rustc_macro_transparency") - })? - .token_tree_value()? - .token_trees - { - [tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &*i.text { - "transparent" => Some(Transparency::Transparent), - "semitransparent" => Some(Transparency::SemiTransparent), - "opaque" => Some(Transparency::Opaque), - _ => None, - }, - _ => None, - } - }; - let toolchain = crate_data.toolchain.as_ref(); - let new_meta_vars = toolchain.as_ref().map_or(false, |version| { - REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( - &base_db::Version { - pre: base_db::Prerelease::EMPTY, - build: base_db::BuildMetadata::EMPTY, - major: version.major, - minor: version.minor, - patch: version.patch, - }, - ) - }); - - let (mac, transparency) = match id.to_ptr(db).to_node(&root) { - ast::Macro::MacroRules(macro_rules) => ( - match macro_rules.token_tree() { - Some(arg) => { - let tt = mbe::syntax_node_to_token_tree( - arg.syntax(), - map.as_ref(), - map.span_for_range(macro_rules.macro_rules_token().unwrap().text_range()), - ); - - mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars) - } - None => mbe::DeclarativeMacro::from_err( - mbe::ParseError::Expected("expected a token tree".into()), - is_2021, - ), - }, - transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent), - ), - ast::Macro::MacroDef(macro_def) => ( - match macro_def.body() { - Some(arg) => { - let tt = mbe::syntax_node_to_token_tree( - arg.syntax(), - map.as_ref(), - map.span_for_range(macro_def.macro_token().unwrap().text_range()), - ); - - mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars) - } - None => mbe::DeclarativeMacro::from_err( - mbe::ParseError::Expected("expected a token tree".into()), - is_2021, - ), - }, - transparency(¯o_def).unwrap_or(Transparency::Opaque), - ), - }; - Arc::new(DeclarativeMacroExpander { mac, transparency }) -} - -fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander { - match id.kind { - MacroDefKind::Declarative(ast_id) => { - TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id)) +impl TokenExpander { + fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander { + match id.kind { + MacroDefKind::Declarative(ast_id) => { + TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id)) + } + MacroDefKind::BuiltIn(expander, _) => TokenExpander::BuiltIn(expander), + MacroDefKind::BuiltInAttr(expander, _) => TokenExpander::BuiltInAttr(expander), + MacroDefKind::BuiltInDerive(expander, _) => TokenExpander::BuiltInDerive(expander), + MacroDefKind::BuiltInEager(expander, ..) => TokenExpander::BuiltInEager(expander), + MacroDefKind::ProcMacro(expander, ..) => TokenExpander::ProcMacro(expander), } - MacroDefKind::BuiltIn(expander, _) => TokenExpander::BuiltIn(expander), - MacroDefKind::BuiltInAttr(expander, _) => TokenExpander::BuiltInAttr(expander), - MacroDefKind::BuiltInDerive(expander, _) => TokenExpander::BuiltInDerive(expander), - MacroDefKind::BuiltInEager(expander, ..) => TokenExpander::BuiltInEager(expander), - MacroDefKind::ProcMacro(expander, ..) => TokenExpander::ProcMacro(expander), } } @@ -862,40 +675,3 @@ fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> { fn setup_syntax_context_root(db: &dyn ExpandDatabase) { db.intern_syntax_context(SyntaxContextData::root()); } - -fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String { - let mut s = String::from("Expansions:"); - let mut entries = InternMacroCallLookupQuery.in_db(db).entries::<Vec<_>>(); - entries.sort_by_key(|e| e.key); - for e in entries { - let id = e.key; - let expn_data = e.value.as_ref().unwrap(); - s.push_str(&format!( - "\n{:?}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}", - id, - expn_data.kind.file_id(), - expn_data.call_site, - SyntaxContextId::ROOT, // FIXME expn_data.def_site, - expn_data.kind.descr(), - )); - } - - s.push_str("\n\nSyntaxContexts:\n"); - let mut entries = InternSyntaxContextLookupQuery.in_db(db).entries::<Vec<_>>(); - entries.sort_by_key(|e| e.key); - for e in entries { - struct SyntaxContextDebug<'a>( - &'a dyn ExpandDatabase, - SyntaxContextId, - &'a SyntaxContextData, - ); - - impl<'a> std::fmt::Debug for SyntaxContextDebug<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.2.fancy_debug(self.1, self.0, f) - } - } - stdx::format_to!(s, "{:?}\n", SyntaxContextDebug(db, e.key, &e.value.unwrap())); - } - s -} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs new file mode 100644 index 00000000000..37084ee8b93 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -0,0 +1,177 @@ +//! Compiled declarative macro expanders (`macro_rules!`` and `macro`) +use std::sync::OnceLock; + +use base_db::{CrateId, Edition, VersionReq}; +use span::{MacroCallId, Span}; +use syntax::{ast, AstNode}; +use triomphe::Arc; + +use crate::{ + attrs::RawAttrs, + db::ExpandDatabase, + hygiene::{apply_mark, Transparency}, + tt, AstId, ExpandError, ExpandResult, +}; + +/// Old-style `macro_rules` or the new macros 2.0 +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct DeclarativeMacroExpander { + pub mac: mbe::DeclarativeMacro<span::Span>, + pub transparency: Transparency, +} + +// FIXME: Remove this once we drop support for 1.76 +static REQUIREMENT: OnceLock<VersionReq> = OnceLock::new(); + +impl DeclarativeMacroExpander { + pub fn expand( + &self, + db: &dyn ExpandDatabase, + tt: tt::Subtree, + call_id: MacroCallId, + ) -> ExpandResult<tt::Subtree> { + let loc = db.lookup_intern_macro_call(call_id); + let toolchain = &db.crate_graph()[loc.def.krate].toolchain; + let new_meta_vars = toolchain.as_ref().map_or(false, |version| { + REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( + &base_db::Version { + pre: base_db::Prerelease::EMPTY, + build: base_db::BuildMetadata::EMPTY, + major: version.major, + minor: version.minor, + patch: version.patch, + }, + ) + }); + match self.mac.err() { + Some(e) => ExpandResult::new( + tt::Subtree::empty(tt::DelimSpan { open: loc.call_site, close: loc.call_site }), + ExpandError::other(format!("invalid macro definition: {e}")), + ), + None => self + .mac + .expand( + &tt, + |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency), + new_meta_vars, + loc.call_site, + ) + .map_err(Into::into), + } + } + + pub fn expand_unhygienic( + &self, + db: &dyn ExpandDatabase, + tt: tt::Subtree, + krate: CrateId, + call_site: Span, + ) -> ExpandResult<tt::Subtree> { + let toolchain = &db.crate_graph()[krate].toolchain; + let new_meta_vars = toolchain.as_ref().map_or(false, |version| { + REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( + &base_db::Version { + pre: base_db::Prerelease::EMPTY, + build: base_db::BuildMetadata::EMPTY, + major: version.major, + minor: version.minor, + patch: version.patch, + }, + ) + }); + match self.mac.err() { + Some(e) => ExpandResult::new( + tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + ExpandError::other(format!("invalid macro definition: {e}")), + ), + None => self.mac.expand(&tt, |_| (), new_meta_vars, call_site).map_err(Into::into), + } + } + + pub(crate) fn expander( + db: &dyn ExpandDatabase, + def_crate: CrateId, + id: AstId<ast::Macro>, + ) -> Arc<DeclarativeMacroExpander> { + let crate_data = &db.crate_graph()[def_crate]; + let is_2021 = crate_data.edition >= Edition::Edition2021; + let (root, map) = crate::db::parse_with_map(db, id.file_id); + let root = root.syntax_node(); + + let transparency = |node| { + // ... would be nice to have the item tree here + let attrs = RawAttrs::new(db, node, map.as_ref()).filter(db, def_crate); + match &*attrs + .iter() + .find(|it| { + it.path.as_ident().and_then(|it| it.as_str()) + == Some("rustc_macro_transparency") + })? + .token_tree_value()? + .token_trees + { + [tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &*i.text { + "transparent" => Some(Transparency::Transparent), + "semitransparent" => Some(Transparency::SemiTransparent), + "opaque" => Some(Transparency::Opaque), + _ => None, + }, + _ => None, + } + }; + let toolchain = crate_data.toolchain.as_ref(); + let new_meta_vars = toolchain.as_ref().map_or(false, |version| { + REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( + &base_db::Version { + pre: base_db::Prerelease::EMPTY, + build: base_db::BuildMetadata::EMPTY, + major: version.major, + minor: version.minor, + patch: version.patch, + }, + ) + }); + + let (mac, transparency) = match id.to_ptr(db).to_node(&root) { + ast::Macro::MacroRules(macro_rules) => ( + match macro_rules.token_tree() { + Some(arg) => { + let tt = mbe::syntax_node_to_token_tree( + arg.syntax(), + map.as_ref(), + map.span_for_range( + macro_rules.macro_rules_token().unwrap().text_range(), + ), + ); + + mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars) + } + None => mbe::DeclarativeMacro::from_err( + mbe::ParseError::Expected("expected a token tree".into()), + is_2021, + ), + }, + transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent), + ), + ast::Macro::MacroDef(macro_def) => ( + match macro_def.body() { + Some(arg) => { + let tt = mbe::syntax_node_to_token_tree( + arg.syntax(), + map.as_ref(), + map.span_for_range(macro_def.macro_token().unwrap().text_range()), + ); + + mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars) + } + None => mbe::DeclarativeMacro::from_err( + mbe::ParseError::Expected("expected a token tree".into()), + is_2021, + ), + }, + transparency(¯o_def).unwrap_or(Transparency::Opaque), + ), + }; + Arc::new(DeclarativeMacroExpander { mac, transparency }) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs index 57921543c4b..8ddaa3f3039 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs @@ -245,3 +245,43 @@ pub fn marks_rev( }) .map(|ctx| ctx.outer_mark(db)) } + +pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String { + use crate::db::{InternMacroCallLookupQuery, InternSyntaxContextLookupQuery}; + use base_db::salsa::debug::DebugQueryTable; + + let mut s = String::from("Expansions:"); + let mut entries = InternMacroCallLookupQuery.in_db(db).entries::<Vec<_>>(); + entries.sort_by_key(|e| e.key); + for e in entries { + let id = e.key; + let expn_data = e.value.as_ref().unwrap(); + s.push_str(&format!( + "\n{:?}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}", + id, + expn_data.kind.file_id(), + expn_data.call_site, + SyntaxContextId::ROOT, // FIXME expn_data.def_site, + expn_data.kind.descr(), + )); + } + + s.push_str("\n\nSyntaxContexts:\n"); + let mut entries = InternSyntaxContextLookupQuery.in_db(db).entries::<Vec<_>>(); + entries.sort_by_key(|e| e.key); + for e in entries { + struct SyntaxContextDebug<'a>( + &'a dyn ExpandDatabase, + SyntaxContextId, + &'a SyntaxContextData, + ); + + impl<'a> std::fmt::Debug for SyntaxContextDebug<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.2.fancy_debug(self.1, self.0, f) + } + } + stdx::format_to!(s, "{:?}\n", SyntaxContextDebug(db, e.key, &e.value.unwrap())); + } + s +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index bd216ccca82..05f12527a44 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -11,16 +11,18 @@ pub mod attrs; pub mod builtin_attr_macro; pub mod builtin_derive_macro; pub mod builtin_fn_macro; +pub mod change; pub mod db; +pub mod declarative; pub mod eager; pub mod files; -pub mod change; pub mod hygiene; pub mod mod_path; pub mod name; pub mod proc_macro; pub mod quote; pub mod span_map; + mod fixup; use attrs::collect_attrs; @@ -167,7 +169,8 @@ pub struct MacroCallLoc { pub krate: CrateId, /// Some if this is a macro call for an eager macro. Note that this is `None` /// for the eager input macro file. - // FIXME: This seems bad to save in an interned structure + // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing + // leakage problems here eager: Option<Arc<EagerCallInfo>>, pub kind: MacroCallKind, pub call_site: Span, @@ -220,7 +223,7 @@ pub enum MacroCallKind { }, Attr { ast_id: AstId<ast::Item>, - // FIXME: This is being interned, subtrees can very quickly differ just slightly causing + // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing // leakage problems here attr_args: Option<Arc<tt::Subtree>>, /// Syntactical index of the invoking `#[attribute]`. diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 47a587e407c..0eb1fc1eb50 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -10,6 +10,7 @@ use crate::{ hygiene::{marks_rev, SyntaxContextExt, Transparency}, name::{known, AsName, Name}, span_map::SpanMapRef, + tt, }; use base_db::CrateId; use smallvec::SmallVec; @@ -39,7 +40,7 @@ pub enum PathKind { Crate, /// Absolute path (::foo) Abs, - // FIXME: Remove this + // FIXME: Can we remove this somehow? /// `$crate` from macro expansion DollarCrate(CrateId), } @@ -50,11 +51,16 @@ impl ModPath { path: ast::Path, span_map: SpanMapRef<'_>, ) -> Option<ModPath> { - convert_path(db, None, path, span_map) + convert_path(db, path, span_map) + } + + pub fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option<ModPath> { + convert_path_tt(db, tt) } pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath { - let segments = segments.into_iter().collect(); + let mut segments: SmallVec<_> = segments.into_iter().collect(); + segments.shrink_to_fit(); ModPath { kind, segments } } @@ -193,22 +199,15 @@ fn display_fmt_path( fn convert_path( db: &dyn ExpandDatabase, - prefix: Option<ModPath>, path: ast::Path, span_map: SpanMapRef<'_>, ) -> Option<ModPath> { - let prefix = match path.qualifier() { - Some(qual) => Some(convert_path(db, prefix, qual, span_map)?), - None => prefix, - }; + let mut segments = path.segments(); - let segment = path.segment()?; + let segment = &segments.next()?; let mut mod_path = match segment.kind()? { ast::PathSegmentKind::Name(name_ref) => { if name_ref.text() == "$crate" { - if prefix.is_some() { - return None; - } ModPath::from_kind( resolve_crate_root( db, @@ -218,41 +217,36 @@ fn convert_path( .unwrap_or(PathKind::Crate), ) } else { - let mut res = prefix.unwrap_or_else(|| { - ModPath::from_kind( - segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs), - ) - }); + let mut res = ModPath::from_kind( + segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs), + ); res.segments.push(name_ref.as_name()); res } } ast::PathSegmentKind::SelfTypeKw => { - if prefix.is_some() { - return None; - } ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE)) } - ast::PathSegmentKind::CrateKw => { - if prefix.is_some() { - return None; - } - ModPath::from_segments(PathKind::Crate, iter::empty()) - } - ast::PathSegmentKind::SelfKw => { - if prefix.is_some() { - return None; - } - ModPath::from_segments(PathKind::Super(0), iter::empty()) - } + ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()), + ast::PathSegmentKind::SelfKw => ModPath::from_segments(PathKind::Super(0), iter::empty()), ast::PathSegmentKind::SuperKw => { - let nested_super_count = match prefix.map(|p| p.kind) { - Some(PathKind::Super(n)) => n, - Some(_) => return None, - None => 0, - }; + let mut deg = 1; + let mut next_segment = None; + while let Some(segment) = segments.next() { + match segment.kind()? { + ast::PathSegmentKind::SuperKw => deg += 1, + ast::PathSegmentKind::Name(name) => { + next_segment = Some(name.as_name()); + break; + } + ast::PathSegmentKind::Type { .. } + | ast::PathSegmentKind::SelfTypeKw + | ast::PathSegmentKind::SelfKw + | ast::PathSegmentKind::CrateKw => return None, + } + } - ModPath::from_segments(PathKind::Super(nested_super_count + 1), iter::empty()) + ModPath::from_segments(PathKind::Super(deg), next_segment) } ast::PathSegmentKind::Type { .. } => { // not allowed in imports @@ -260,6 +254,14 @@ fn convert_path( } }; + for segment in segments { + let name = match segment.kind()? { + ast::PathSegmentKind::Name(name) => name.as_name(), + _ => return None, + }; + mod_path.segments.push(name); + } + // handle local_inner_macros : // Basically, even in rustc it is quite hacky: // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456 @@ -281,6 +283,46 @@ fn convert_path( Some(mod_path) } +fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option<ModPath> { + let mut leafs = tt.iter().filter_map(|tt| match tt { + tt::TokenTree::Leaf(leaf) => Some(leaf), + tt::TokenTree::Subtree(_) => None, + }); + let mut segments = smallvec::smallvec![]; + let kind = match leafs.next()? { + tt::Leaf::Punct(tt::Punct { char: ':', .. }) => match leafs.next()? { + tt::Leaf::Punct(tt::Punct { char: ':', .. }) => PathKind::Abs, + _ => return None, + }, + tt::Leaf::Ident(tt::Ident { text, span }) if text == "$crate" => { + resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate) + } + tt::Leaf::Ident(tt::Ident { text, .. }) if text == "self" => PathKind::Super(0), + tt::Leaf::Ident(tt::Ident { text, .. }) if text == "super" => { + let mut deg = 1; + while let Some(tt::Leaf::Ident(tt::Ident { text, .. })) = leafs.next() { + if text != "super" { + segments.push(Name::new_text_dont_use(text.clone())); + break; + } + deg += 1; + } + PathKind::Super(deg) + } + tt::Leaf::Ident(tt::Ident { text, .. }) if text == "crate" => PathKind::Crate, + tt::Leaf::Ident(ident) => { + segments.push(Name::new_text_dont_use(ident.text.clone())); + PathKind::Plain + } + _ => return None, + }; + segments.extend(leafs.filter_map(|leaf| match leaf { + ::tt::Leaf::Ident(ident) => Some(Name::new_text_dont_use(ident.text.clone())), + _ => None, + })); + Some(ModPath { kind, segments }) +} + pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContextId) -> Option<CrateId> { // When resolving `$crate` from a `macro_rules!` invoked in a `macro`, // we don't want to pretend that the `macro_rules!` definition is in the `macro` diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs index 4ec6e657f9e..8e624f5585d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs @@ -1,10 +1,12 @@ //! Span maps for real files and macro expansions. -use span::Span; -use syntax::TextRange; +use span::{FileId, HirFileId, HirFileIdRepr, Span}; +use syntax::{AstNode, TextRange}; use triomphe::Arc; pub use span::RealSpanMap; +use crate::db::ExpandDatabase; + pub type ExpansionSpanMap = span::SpanMap<Span>; /// Spanmap for a macro file or a real file @@ -34,7 +36,6 @@ impl mbe::SpanMapper<Span> for SpanMapRef<'_> { self.span_for_range(range) } } - impl SpanMap { pub fn span_for_range(&self, range: TextRange) -> Span { match self { @@ -53,6 +54,16 @@ impl SpanMap { Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map), } } + + #[inline] + pub(crate) fn new(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap { + match file_id.repr() { + HirFileIdRepr::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)), + HirFileIdRepr::MacroFile(m) => { + SpanMap::ExpansionSpanMap(db.parse_macro_expansion(m).value.1) + } + } + } } impl SpanMapRef<'_> { @@ -63,3 +74,23 @@ impl SpanMapRef<'_> { } } } + +pub(crate) fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc<RealSpanMap> { + use syntax::ast::HasModuleItem; + let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)]; + let ast_id_map = db.ast_id_map(file_id.into()); + let tree = db.parse(file_id).tree(); + // FIXME: Descend into modules and other item containing items that are not annotated with attributes + // and allocate pairs for those as well. This gives us finer grained span anchors resulting in + // better incrementality + pairs.extend( + tree.items() + .map(|item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase())), + ); + + Arc::new(RealSpanMap::from_file( + file_id, + pairs.into_boxed_slice(), + tree.syntax().text_range().end(), + )) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 822a7d3e919..1f8f8744f9e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -36,6 +36,7 @@ indexmap.workspace = true ra-ap-rustc_abi.workspace = true ra-ap-rustc_index.workspace = true +ra-ap-rustc_pattern_analysis.workspace = true # local deps diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs index c1b3619009b..af4d2c9fc04 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs @@ -1,8 +1,8 @@ //! Type inference-based diagnostics. +mod decl_check; mod expr; mod match_check; mod unsafe_check; -mod decl_check; pub use crate::diagnostics::{ decl_check::{incorrect_case, CaseType, IncorrectCase}, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index f1bf162bc6b..530608292e6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -11,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup}; use hir_expand::name; use itertools::Itertools; use rustc_hash::FxHashSet; +use rustc_pattern_analysis::usefulness::{compute_match_usefulness, ValidityConstraint}; use triomphe::Arc; use typed_arena::Arena; @@ -18,8 +19,7 @@ use crate::{ db::HirDatabase, diagnostics::match_check::{ self, - deconstruct_pat::DeconstructedPat, - usefulness::{compute_match_usefulness, MatchCheckCtx}, + pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat}, }, display::HirDisplay, InferenceResult, Ty, TyExt, @@ -152,7 +152,14 @@ impl ExprValidator { } let pattern_arena = Arena::new(); - let cx = MatchCheckCtx::new(self.owner.module(db.upcast()), self.owner, db, &pattern_arena); + let ty_arena = Arena::new(); + let cx = MatchCheckCtx::new( + self.owner.module(db.upcast()), + self.owner, + db, + &pattern_arena, + &ty_arena, + ); let mut m_arms = Vec::with_capacity(arms.len()); let mut has_lowering_errors = false; @@ -178,9 +185,10 @@ impl ExprValidator { // If we had a NotUsefulMatchArm diagnostic, we could // check the usefulness of each pattern as we added it // to the matrix here. - let m_arm = match_check::MatchArm { + let m_arm = pat_analysis::MatchArm { pat: self.lower_pattern(&cx, arm.pat, db, &body, &mut has_lowering_errors), has_guard: arm.guard.is_some(), + arm_data: (), }; m_arms.push(m_arm); if !has_lowering_errors { @@ -197,7 +205,15 @@ impl ExprValidator { return; } - let report = compute_match_usefulness(&cx, &m_arms, scrut_ty); + let report = match compute_match_usefulness( + rustc_pattern_analysis::MatchCtxt { tycx: &cx }, + m_arms.as_slice(), + scrut_ty.clone(), + ValidityConstraint::ValidOnly, + ) { + Ok(report) => report, + Err(void) => match void {}, + }; // FIXME Report unreachable arms // https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200 @@ -213,7 +229,7 @@ impl ExprValidator { fn lower_pattern<'p>( &self, - cx: &MatchCheckCtx<'_, 'p>, + cx: &MatchCheckCtx<'p>, pat: PatId, db: &dyn HirDatabase, body: &Body, @@ -221,7 +237,7 @@ impl ExprValidator { ) -> &'p DeconstructedPat<'p> { let mut patcx = match_check::PatCtxt::new(db, &self.infer, body); let pattern = patcx.lower_pattern(pat); - let pattern = cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern)); + let pattern = cx.pattern_arena.alloc(cx.lower_pat(&pattern)); if !patcx.errors.is_empty() { *have_errors = true; } @@ -364,16 +380,16 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul } fn missing_match_arms<'p>( - cx: &MatchCheckCtx<'_, 'p>, + cx: &MatchCheckCtx<'p>, scrut_ty: &Ty, - witnesses: Vec<DeconstructedPat<'p>>, + witnesses: Vec<WitnessPat<'p>>, arms: &[MatchArm], ) -> String { - struct DisplayWitness<'a, 'p>(&'a DeconstructedPat<'p>, &'a MatchCheckCtx<'a, 'p>); + struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>); impl fmt::Display for DisplayWitness<'_, '_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let DisplayWitness(witness, cx) = *self; - let pat = witness.to_pat(cx); + let pat = cx.hoist_witness_pat(witness); write!(f, "{}", pat.display(cx.db)) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index e4d4536fc93..8d6e502c6ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -7,8 +7,7 @@ mod pat_util; -pub(crate) mod deconstruct_pat; -pub(crate) mod usefulness; +pub(crate) mod pat_analysis; use chalk_ir::Mutability; use hir_def::{ @@ -27,8 +26,6 @@ use crate::{ use self::pat_util::EnumerateAndAdjustIterator; -pub(crate) use self::usefulness::MatchArm; - #[derive(Clone, Debug)] pub(crate) enum PatternError { Unimplemented, @@ -413,98 +410,3 @@ where (self.0)(f) } } - -pub(crate) trait PatternFoldable: Sized { - fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { - self.super_fold_with(folder) - } - - fn super_fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self; -} - -pub(crate) trait PatternFolder: Sized { - fn fold_pattern(&mut self, pattern: &Pat) -> Pat { - pattern.super_fold_with(self) - } - - fn fold_pattern_kind(&mut self, kind: &PatKind) -> PatKind { - kind.super_fold_with(self) - } -} - -impl<T: PatternFoldable> PatternFoldable for Box<T> { - fn super_fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { - let content: T = (**self).fold_with(folder); - Box::new(content) - } -} - -impl<T: PatternFoldable> PatternFoldable for Vec<T> { - fn super_fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { - self.iter().map(|t| t.fold_with(folder)).collect() - } -} - -impl<T: PatternFoldable> PatternFoldable for Option<T> { - fn super_fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { - self.as_ref().map(|t| t.fold_with(folder)) - } -} - -macro_rules! clone_impls { - ($($ty:ty),+) => { - $( - impl PatternFoldable for $ty { - fn super_fold_with<F: PatternFolder>(&self, _: &mut F) -> Self { - Clone::clone(self) - } - } - )+ - } -} - -clone_impls! { LocalFieldId, Ty, Substitution, EnumVariantId } - -impl PatternFoldable for FieldPat { - fn super_fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { - FieldPat { field: self.field.fold_with(folder), pattern: self.pattern.fold_with(folder) } - } -} - -impl PatternFoldable for Pat { - fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { - folder.fold_pattern(self) - } - - fn super_fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { - Pat { ty: self.ty.fold_with(folder), kind: self.kind.fold_with(folder) } - } -} - -impl PatternFoldable for PatKind { - fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { - folder.fold_pattern_kind(self) - } - - fn super_fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { - match self { - PatKind::Wild => PatKind::Wild, - PatKind::Binding { name, subpattern } => { - PatKind::Binding { name: name.clone(), subpattern: subpattern.fold_with(folder) } - } - PatKind::Variant { substs, enum_variant, subpatterns } => PatKind::Variant { - substs: substs.fold_with(folder), - enum_variant: enum_variant.fold_with(folder), - subpatterns: subpatterns.fold_with(folder), - }, - PatKind::Leaf { subpatterns } => { - PatKind::Leaf { subpatterns: subpatterns.fold_with(folder) } - } - PatKind::Deref { subpattern } => { - PatKind::Deref { subpattern: subpattern.fold_with(folder) } - } - &PatKind::LiteralBool { value } => PatKind::LiteralBool { value }, - PatKind::Or { pats } => PatKind::Or { pats: pats.fold_with(folder) }, - } - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs deleted file mode 100644 index f066f8b798d..00000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs +++ /dev/null @@ -1,1098 +0,0 @@ -//! [`super::usefulness`] explains most of what is happening in this file. As explained there, -//! values and patterns are made from constructors applied to fields. This file defines a -//! `Constructor` enum, a `Fields` struct, and various operations to manipulate them and convert -//! them from/to patterns. -//! -//! There's one idea that is not detailed in [`super::usefulness`] because the details are not -//! needed there: _constructor splitting_. -//! -//! # Constructor splitting -//! -//! The idea is as follows: given a constructor `c` and a matrix, we want to specialize in turn -//! with all the value constructors that are covered by `c`, and compute usefulness for each. -//! Instead of listing all those constructors (which is intractable), we group those value -//! constructors together as much as possible. Example: -//! -//! ``` -//! match (0, false) { -//! (0 ..=100, true) => {} // `p_1` -//! (50..=150, false) => {} // `p_2` -//! (0 ..=200, _) => {} // `q` -//! } -//! ``` -//! -//! The naive approach would try all numbers in the range `0..=200`. But we can be a lot more -//! clever: `0` and `1` for example will match the exact same rows, and return equivalent -//! witnesses. In fact all of `0..50` would. We can thus restrict our exploration to 4 -//! constructors: `0..50`, `50..=100`, `101..=150` and `151..=200`. That is enough and infinitely -//! more tractable. -//! -//! We capture this idea in a function `split(p_1 ... p_n, c)` which returns a list of constructors -//! `c'` covered by `c`. Given such a `c'`, we require that all value ctors `c''` covered by `c'` -//! return an equivalent set of witnesses after specializing and computing usefulness. -//! In the example above, witnesses for specializing by `c''` covered by `0..50` will only differ -//! in their first element. -//! -//! We usually also ask that the `c'` together cover all of the original `c`. However we allow -//! skipping some constructors as long as it doesn't change whether the resulting list of witnesses -//! is empty of not. We use this in the wildcard `_` case. -//! -//! Splitting is implemented in the [`Constructor::split`] function. We don't do splitting for -//! or-patterns; instead we just try the alternatives one-by-one. For details on splitting -//! wildcards, see [`SplitWildcard`]; for integer ranges, see [`SplitIntRange`]. - -use std::{ - cell::Cell, - cmp::{max, min}, - iter::once, - ops::RangeInclusive, -}; - -use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId}; -use smallvec::{smallvec, SmallVec}; -use stdx::never; - -use crate::{ - infer::normalize, inhabitedness::is_enum_variant_uninhabited_from, AdtId, Interner, Scalar, Ty, - TyExt, TyKind, -}; - -use super::{ - is_box, - usefulness::{helper::Captures, MatchCheckCtx, PatCtxt}, - FieldPat, Pat, PatKind, -}; - -use self::Constructor::*; - -/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. -fn expand_or_pat(pat: &Pat) -> Vec<&Pat> { - fn expand<'p>(pat: &'p Pat, vec: &mut Vec<&'p Pat>) { - if let PatKind::Or { pats } = pat.kind.as_ref() { - for pat in pats { - expand(pat, vec); - } - } else { - vec.push(pat) - } - } - - let mut pats = Vec::new(); - expand(pat, &mut pats); - pats -} - -/// [Constructor] uses this in unimplemented variants. -/// It allows porting match expressions from upstream algorithm without losing semantics. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(super) enum Void {} - -/// An inclusive interval, used for precise integer exhaustiveness checking. -/// `IntRange`s always store a contiguous range. This means that values are -/// encoded such that `0` encodes the minimum value for the integer, -/// regardless of the signedness. -/// For example, the pattern `-128..=127i8` is encoded as `0..=255`. -/// This makes comparisons and arithmetic on interval endpoints much more -/// straightforward. See `signed_bias` for details. -/// -/// `IntRange` is never used to encode an empty range or a "range" that wraps -/// around the (offset) space: i.e., `range.lo <= range.hi`. -#[derive(Clone, Debug, PartialEq, Eq)] -pub(super) struct IntRange { - range: RangeInclusive<u128>, -} - -impl IntRange { - #[inline] - fn is_integral(ty: &Ty) -> bool { - matches!( - ty.kind(Interner), - TyKind::Scalar(Scalar::Char | Scalar::Int(_) | Scalar::Uint(_) | Scalar::Bool) - ) - } - - fn is_singleton(&self) -> bool { - self.range.start() == self.range.end() - } - - fn boundaries(&self) -> (u128, u128) { - (*self.range.start(), *self.range.end()) - } - - #[inline] - fn from_bool(value: bool) -> IntRange { - let val = value as u128; - IntRange { range: val..=val } - } - - #[inline] - fn from_range(lo: u128, hi: u128, scalar_ty: Scalar) -> IntRange { - match scalar_ty { - Scalar::Bool => IntRange { range: lo..=hi }, - _ => unimplemented!(), - } - } - - fn is_subrange(&self, other: &Self) -> bool { - other.range.start() <= self.range.start() && self.range.end() <= other.range.end() - } - - fn intersection(&self, other: &Self) -> Option<Self> { - let (lo, hi) = self.boundaries(); - let (other_lo, other_hi) = other.boundaries(); - if lo <= other_hi && other_lo <= hi { - Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi) }) - } else { - None - } - } - - fn to_pat(&self, _cx: &MatchCheckCtx<'_, '_>, ty: Ty) -> Pat { - match ty.kind(Interner) { - TyKind::Scalar(Scalar::Bool) => { - let kind = match self.boundaries() { - (0, 0) => PatKind::LiteralBool { value: false }, - (1, 1) => PatKind::LiteralBool { value: true }, - (0, 1) => PatKind::Wild, - (lo, hi) => { - never!("bad range for bool pattern: {}..={}", lo, hi); - PatKind::Wild - } - }; - Pat { ty, kind: kind.into() } - } - _ => unimplemented!(), - } - } - - /// See `Constructor::is_covered_by` - fn is_covered_by(&self, other: &Self) -> bool { - if self.intersection(other).is_some() { - // Constructor splitting should ensure that all intersections we encounter are actually - // inclusions. - assert!(self.is_subrange(other)); - true - } else { - false - } - } -} - -/// Represents a border between 2 integers. Because the intervals spanning borders must be able to -/// cover every integer, we need to be able to represent 2^128 + 1 such borders. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum IntBorder { - JustBefore(u128), - AfterMax, -} - -/// A range of integers that is partitioned into disjoint subranges. This does constructor -/// splitting for integer ranges as explained at the top of the file. -/// -/// This is fed multiple ranges, and returns an output that covers the input, but is split so that -/// the only intersections between an output range and a seen range are inclusions. No output range -/// straddles the boundary of one of the inputs. -/// -/// The following input: -/// ``` -/// |-------------------------| // `self` -/// |------| |----------| |----| -/// |-------| |-------| -/// ``` -/// would be iterated over as follows: -/// ``` -/// ||---|--||-|---|---|---|--| -/// ``` -#[derive(Debug, Clone)] -struct SplitIntRange { - /// The range we are splitting - range: IntRange, - /// The borders of ranges we have seen. They are all contained within `range`. This is kept - /// sorted. - borders: Vec<IntBorder>, -} - -impl SplitIntRange { - fn new(range: IntRange) -> Self { - SplitIntRange { range, borders: Vec::new() } - } - - /// Internal use - fn to_borders(r: IntRange) -> [IntBorder; 2] { - use IntBorder::*; - let (lo, hi) = r.boundaries(); - let lo = JustBefore(lo); - let hi = match hi.checked_add(1) { - Some(m) => JustBefore(m), - None => AfterMax, - }; - [lo, hi] - } - - /// Add ranges relative to which we split. - fn split(&mut self, ranges: impl Iterator<Item = IntRange>) { - let this_range = &self.range; - let included_ranges = ranges.filter_map(|r| this_range.intersection(&r)); - let included_borders = included_ranges.flat_map(|r| { - let borders = Self::to_borders(r); - once(borders[0]).chain(once(borders[1])) - }); - self.borders.extend(included_borders); - self.borders.sort_unstable(); - } - - /// Iterate over the contained ranges. - fn iter(&self) -> impl Iterator<Item = IntRange> + '_ { - use IntBorder::*; - - let self_range = Self::to_borders(self.range.clone()); - // Start with the start of the range. - let mut prev_border = self_range[0]; - self.borders - .iter() - .copied() - // End with the end of the range. - .chain(once(self_range[1])) - // List pairs of adjacent borders. - .map(move |border| { - let ret = (prev_border, border); - prev_border = border; - ret - }) - // Skip duplicates. - .filter(|(prev_border, border)| prev_border != border) - // Finally, convert to ranges. - .map(|(prev_border, border)| { - let range = match (prev_border, border) { - (JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1), - (JustBefore(n), AfterMax) => n..=u128::MAX, - _ => unreachable!(), // Ruled out by the sorting and filtering we did - }; - IntRange { range } - }) - } -} - -/// A constructor for array and slice patterns. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(super) struct Slice { - _unimplemented: Void, -} - -impl Slice { - fn arity(self) -> usize { - match self._unimplemented {} - } - - /// See `Constructor::is_covered_by` - fn is_covered_by(self, _other: Self) -> bool { - match self._unimplemented {} - } -} - -/// A value can be decomposed into a constructor applied to some fields. This struct represents -/// the constructor. See also `Fields`. -/// -/// `pat_constructor` retrieves the constructor corresponding to a pattern. -/// `specialize_constructor` returns the list of fields corresponding to a pattern, given a -/// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and -/// `Fields`. -#[allow(dead_code)] -#[derive(Clone, Debug, PartialEq)] -pub(super) enum Constructor { - /// The constructor for patterns that have a single constructor, like tuples, struct patterns - /// and fixed-length arrays. - Single, - /// Enum variants. - Variant(EnumVariantId), - /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). - IntRange(IntRange), - /// Ranges of floating-point literal values (`2.0..=5.2`). - FloatRange(Void), - /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. - Str(Void), - /// Array and slice patterns. - Slice(Slice), - /// Constants that must not be matched structurally. They are treated as black - /// boxes for the purposes of exhaustiveness: we must not inspect them, and they - /// don't count towards making a match exhaustive. - Opaque, - /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used - /// for those types for which we cannot list constructors explicitly, like `f64` and `str`. - NonExhaustive, - /// Stands for constructors that are not seen in the matrix, as explained in the documentation - /// for [`SplitWildcard`]. The carried `bool` is used for the `non_exhaustive_omitted_patterns` - /// lint. - Missing { nonexhaustive_enum_missing_real_variants: bool }, - /// Wildcard pattern. - Wildcard, - /// Or-pattern. - Or, -} - -impl Constructor { - pub(super) fn is_wildcard(&self) -> bool { - matches!(self, Wildcard) - } - - pub(super) fn is_non_exhaustive(&self) -> bool { - matches!(self, NonExhaustive) - } - - fn as_int_range(&self) -> Option<&IntRange> { - match self { - IntRange(range) => Some(range), - _ => None, - } - } - - fn as_slice(&self) -> Option<Slice> { - match self { - Slice(slice) => Some(*slice), - _ => None, - } - } - - pub(super) fn is_unstable_variant(&self, _pcx: PatCtxt<'_, '_>) -> bool { - false //FIXME: implement this - } - - pub(super) fn is_doc_hidden_variant(&self, _pcx: PatCtxt<'_, '_>) -> bool { - false //FIXME: implement this - } - - fn variant_id_for_adt(&self, adt: hir_def::AdtId) -> VariantId { - match *self { - Variant(id) => id.into(), - Single => { - assert!(!matches!(adt, hir_def::AdtId::EnumId(_))); - match adt { - hir_def::AdtId::EnumId(_) => unreachable!(), - hir_def::AdtId::StructId(id) => id.into(), - hir_def::AdtId::UnionId(id) => id.into(), - } - } - _ => panic!("bad constructor {self:?} for adt {adt:?}"), - } - } - - /// The number of fields for this constructor. This must be kept in sync with - /// `Fields::wildcards`. - pub(super) fn arity(&self, pcx: PatCtxt<'_, '_>) -> usize { - match self { - Single | Variant(_) => match *pcx.ty.kind(Interner) { - TyKind::Tuple(arity, ..) => arity, - TyKind::Ref(..) => 1, - TyKind::Adt(adt, ..) => { - if is_box(pcx.cx.db, adt.0) { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - 1 - } else { - let variant = self.variant_id_for_adt(adt.0); - Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count() - } - } - _ => { - never!("Unexpected type for `Single` constructor: {:?}", pcx.ty); - 0 - } - }, - Slice(slice) => slice.arity(), - Str(..) - | FloatRange(..) - | IntRange(..) - | NonExhaustive - | Opaque - | Missing { .. } - | Wildcard => 0, - Or => { - never!("The `Or` constructor doesn't have a fixed arity"); - 0 - } - } - } - - /// Some constructors (namely `Wildcard`, `IntRange` and `Slice`) actually stand for a set of actual - /// constructors (like variants, integers or fixed-sized slices). When specializing for these - /// constructors, we want to be specialising for the actual underlying constructors. - /// Naively, we would simply return the list of constructors they correspond to. We instead are - /// more clever: if there are constructors that we know will behave the same wrt the current - /// matrix, we keep them grouped. For example, all slices of a sufficiently large length - /// will either be all useful or all non-useful with a given matrix. - /// - /// See the branches for details on how the splitting is done. - /// - /// This function may discard some irrelevant constructors if this preserves behavior and - /// diagnostics. Eg. for the `_` case, we ignore the constructors already present in the - /// matrix, unless all of them are. - pub(super) fn split<'a>( - &self, - pcx: PatCtxt<'_, '_>, - ctors: impl Iterator<Item = &'a Constructor> + Clone, - ) -> SmallVec<[Self; 1]> { - match self { - Wildcard => { - let mut split_wildcard = SplitWildcard::new(pcx); - split_wildcard.split(pcx, ctors); - split_wildcard.into_ctors(pcx) - } - // Fast-track if the range is trivial. In particular, we don't do the overlapping - // ranges check. - IntRange(ctor_range) if !ctor_range.is_singleton() => { - let mut split_range = SplitIntRange::new(ctor_range.clone()); - let int_ranges = ctors.filter_map(|ctor| ctor.as_int_range()); - split_range.split(int_ranges.cloned()); - split_range.iter().map(IntRange).collect() - } - Slice(slice) => match slice._unimplemented {}, - // Any other constructor can be used unchanged. - _ => smallvec![self.clone()], - } - } - - /// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`. - /// For the simple cases, this is simply checking for equality. For the "grouped" constructors, - /// this checks for inclusion. - // We inline because this has a single call site in `Matrix::specialize_constructor`. - #[inline] - pub(super) fn is_covered_by(&self, _pcx: PatCtxt<'_, '_>, other: &Self) -> bool { - // This must be kept in sync with `is_covered_by_any`. - match (self, other) { - // Wildcards cover anything - (_, Wildcard) => true, - // The missing ctors are not covered by anything in the matrix except wildcards. - (Missing { .. } | Wildcard, _) => false, - - (Single, Single) => true, - (Variant(self_id), Variant(other_id)) => self_id == other_id, - - (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range), - (FloatRange(void), FloatRange(..)) => match *void {}, - (Str(void), Str(..)) => match *void {}, - (Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice), - - // We are trying to inspect an opaque constant. Thus we skip the row. - (Opaque, _) | (_, Opaque) => false, - // Only a wildcard pattern can match the special extra constructor. - (NonExhaustive, _) => false, - - _ => { - never!("trying to compare incompatible constructors {:?} and {:?}", self, other); - // Continue with 'whatever is covered' supposed to result in false no-error diagnostic. - true - } - } - } - - /// Faster version of `is_covered_by` when applied to many constructors. `used_ctors` is - /// assumed to be built from `matrix.head_ctors()` with wildcards filtered out, and `self` is - /// assumed to have been split from a wildcard. - fn is_covered_by_any(&self, _pcx: PatCtxt<'_, '_>, used_ctors: &[Constructor]) -> bool { - if used_ctors.is_empty() { - return false; - } - - // This must be kept in sync with `is_covered_by`. - match self { - // If `self` is `Single`, `used_ctors` cannot contain anything else than `Single`s. - Single => !used_ctors.is_empty(), - Variant(_) => used_ctors.iter().any(|c| c == self), - IntRange(range) => used_ctors - .iter() - .filter_map(|c| c.as_int_range()) - .any(|other| range.is_covered_by(other)), - Slice(slice) => used_ctors - .iter() - .filter_map(|c| c.as_slice()) - .any(|other| slice.is_covered_by(other)), - // This constructor is never covered by anything else - NonExhaustive => false, - Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard | Or => { - never!("found unexpected ctor in all_ctors: {:?}", self); - true - } - } - } -} - -/// A wildcard constructor that we split relative to the constructors in the matrix, as explained -/// at the top of the file. -/// -/// A constructor that is not present in the matrix rows will only be covered by the rows that have -/// wildcards. Thus we can group all of those constructors together; we call them "missing -/// constructors". Splitting a wildcard would therefore list all present constructors individually -/// (or grouped if they are integers or slices), and then all missing constructors together as a -/// group. -/// -/// However we can go further: since any constructor will match the wildcard rows, and having more -/// rows can only reduce the amount of usefulness witnesses, we can skip the present constructors -/// and only try the missing ones. -/// This will not preserve the whole list of witnesses, but will preserve whether the list is empty -/// or not. In fact this is quite natural from the point of view of diagnostics too. This is done -/// in `to_ctors`: in some cases we only return `Missing`. -#[derive(Debug)] -pub(super) struct SplitWildcard { - /// Constructors seen in the matrix. - matrix_ctors: Vec<Constructor>, - /// All the constructors for this type - all_ctors: SmallVec<[Constructor; 1]>, -} - -impl SplitWildcard { - pub(super) fn new(pcx: PatCtxt<'_, '_>) -> Self { - let cx = pcx.cx; - let make_range = |start, end, scalar| IntRange(IntRange::from_range(start, end, scalar)); - - // Unhandled types are treated as non-exhaustive. Being explicit here instead of falling - // to catchall arm to ease further implementation. - let unhandled = || smallvec![NonExhaustive]; - - // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, - // arrays and slices we use ranges and variable-length slices when appropriate. - // - // If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that - // are statically impossible. E.g., for `Option<!>`, we do not include `Some(_)` in the - // returned list of constructors. - // Invariant: this is empty if and only if the type is uninhabited (as determined by - // `cx.is_uninhabited()`). - let all_ctors = match pcx.ty.kind(Interner) { - TyKind::Scalar(Scalar::Bool) => smallvec![make_range(0, 1, Scalar::Bool)], - // TyKind::Array(..) if ... => unhandled(), - TyKind::Array(..) | TyKind::Slice(..) => unhandled(), - TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), subst) => { - let enum_data = cx.db.enum_data(*enum_id); - - // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an - // additional "unknown" constructor. - // There is no point in enumerating all possible variants, because the user can't - // actually match against them all themselves. So we always return only the fictitious - // constructor. - // E.g., in an example like: - // - // ``` - // let err: io::ErrorKind = ...; - // match err { - // io::ErrorKind::NotFound => {}, - // } - // ``` - // - // we don't want to show every possible IO error, but instead have only `_` as the - // witness. - let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); - - let is_exhaustive_pat_feature = cx.feature_exhaustive_patterns(); - - // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it - // as though it had an "unknown" constructor to avoid exposing its emptiness. The - // exception is if the pattern is at the top level, because we want empty matches to be - // considered exhaustive. - let is_secretly_empty = enum_data.variants.is_empty() - && !is_exhaustive_pat_feature - && !pcx.is_top_level; - - let mut ctors: SmallVec<[_; 1]> = enum_data - .variants - .iter() - .map(|&(variant, _)| variant) - .filter(|&variant| { - // If `exhaustive_patterns` is enabled, we exclude variants known to be - // uninhabited. - let is_uninhabited = is_exhaustive_pat_feature - && is_enum_variant_uninhabited_from(variant, subst, cx.module, cx.db); - !is_uninhabited - }) - .map(Variant) - .collect(); - - if is_secretly_empty || is_declared_nonexhaustive { - ctors.push(NonExhaustive); - } - ctors - } - TyKind::Scalar(Scalar::Char) => unhandled(), - TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(), - TyKind::Never if !cx.feature_exhaustive_patterns() && !pcx.is_top_level => { - smallvec![NonExhaustive] - } - TyKind::Never => SmallVec::new(), - _ if cx.is_uninhabited(pcx.ty) => SmallVec::new(), - TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single], - // This type is one for which we cannot list constructors, like `str` or `f64`. - _ => smallvec![NonExhaustive], - }; - - SplitWildcard { matrix_ctors: Vec::new(), all_ctors } - } - - /// Pass a set of constructors relative to which to split this one. Don't call twice, it won't - /// do what you want. - pub(super) fn split<'a>( - &mut self, - pcx: PatCtxt<'_, '_>, - ctors: impl Iterator<Item = &'a Constructor> + Clone, - ) { - // Since `all_ctors` never contains wildcards, this won't recurse further. - self.all_ctors = - self.all_ctors.iter().flat_map(|ctor| ctor.split(pcx, ctors.clone())).collect(); - self.matrix_ctors = ctors.filter(|c| !c.is_wildcard()).cloned().collect(); - } - - /// Whether there are any value constructors for this type that are not present in the matrix. - fn any_missing(&self, pcx: PatCtxt<'_, '_>) -> bool { - self.iter_missing(pcx).next().is_some() - } - - /// Iterate over the constructors for this type that are not present in the matrix. - pub(super) fn iter_missing<'a, 'p>( - &'a self, - pcx: PatCtxt<'a, 'p>, - ) -> impl Iterator<Item = &'a Constructor> + Captures<'p> { - self.all_ctors.iter().filter(move |ctor| !ctor.is_covered_by_any(pcx, &self.matrix_ctors)) - } - - /// Return the set of constructors resulting from splitting the wildcard. As explained at the - /// top of the file, if any constructors are missing we can ignore the present ones. - fn into_ctors(self, pcx: PatCtxt<'_, '_>) -> SmallVec<[Constructor; 1]> { - if self.any_missing(pcx) { - // Some constructors are missing, thus we can specialize with the special `Missing` - // constructor, which stands for those constructors that are not seen in the matrix, - // and matches the same rows as any of them (namely the wildcard rows). See the top of - // the file for details. - // However, when all constructors are missing we can also specialize with the full - // `Wildcard` constructor. The difference will depend on what we want in diagnostics. - - // If some constructors are missing, we typically want to report those constructors, - // e.g.: - // ``` - // enum Direction { N, S, E, W } - // let Direction::N = ...; - // ``` - // we can report 3 witnesses: `S`, `E`, and `W`. - // - // However, if the user didn't actually specify a constructor - // in this arm, e.g., in - // ``` - // let x: (Direction, Direction, bool) = ...; - // let (_, _, false) = x; - // ``` - // we don't want to show all 16 possible witnesses `(<direction-1>, <direction-2>, - // true)` - we are satisfied with `(_, _, true)`. So if all constructors are missing we - // prefer to report just a wildcard `_`. - // - // The exception is: if we are at the top-level, for example in an empty match, we - // sometimes prefer reporting the list of constructors instead of just `_`. - let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty); - let ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing { - if pcx.is_non_exhaustive { - Missing { - nonexhaustive_enum_missing_real_variants: self - .iter_missing(pcx) - .any(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx))), - } - } else { - Missing { nonexhaustive_enum_missing_real_variants: false } - } - } else { - Wildcard - }; - return smallvec![ctor]; - } - - // All the constructors are present in the matrix, so we just go through them all. - self.all_ctors - } -} - -/// A value can be decomposed into a constructor applied to some fields. This struct represents -/// those fields, generalized to allow patterns in each field. See also `Constructor`. -/// -/// This is constructed for a constructor using [`Fields::wildcards()`]. The idea is that -/// [`Fields::wildcards()`] constructs a list of fields where all entries are wildcards, and then -/// given a pattern we fill some of the fields with its subpatterns. -/// In the following example `Fields::wildcards` returns `[_, _, _, _]`. Then in -/// `extract_pattern_arguments` we fill some of the entries, and the result is -/// `[Some(0), _, _, _]`. -/// ```rust -/// let x: [Option<u8>; 4] = foo(); -/// match x { -/// [Some(0), ..] => {} -/// } -/// ``` -/// -/// Note that the number of fields of a constructor 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`. For that reason, when you have a `mir::Field` you must use -/// `index_with_declared_idx`. -#[derive(Clone, Copy)] -pub(super) struct Fields<'p> { - fields: &'p [DeconstructedPat<'p>], -} - -impl<'p> Fields<'p> { - fn empty() -> Self { - Fields { fields: &[] } - } - - fn singleton(cx: &MatchCheckCtx<'_, 'p>, field: DeconstructedPat<'p>) -> Self { - let field = cx.pattern_arena.alloc(field); - Fields { fields: std::slice::from_ref(field) } - } - - pub(super) fn from_iter( - cx: &MatchCheckCtx<'_, 'p>, - fields: impl IntoIterator<Item = DeconstructedPat<'p>>, - ) -> Self { - let fields: &[_] = cx.pattern_arena.alloc_extend(fields); - Fields { fields } - } - - fn wildcards_from_tys(cx: &MatchCheckCtx<'_, 'p>, tys: impl IntoIterator<Item = Ty>) -> Self { - Fields::from_iter(cx, tys.into_iter().map(DeconstructedPat::wildcard)) - } - - // 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. - fn list_variant_nonhidden_fields<'a>( - cx: &'a MatchCheckCtx<'a, 'p>, - ty: &'a Ty, - variant: VariantId, - ) -> impl Iterator<Item = (LocalFieldId, Ty)> + Captures<'a> + Captures<'p> { - let (adt, substs) = ty.as_adt().unwrap(); - - let adt_is_local = variant.module(cx.db.upcast()).krate() == cx.module.krate(); - // Whether we must not match the fields of this variant exhaustively. - let is_non_exhaustive = is_field_list_non_exhaustive(variant, cx) && !adt_is_local; - - let visibility = cx.db.field_visibilities(variant); - let field_ty = cx.db.field_types(variant); - let fields_len = variant.variant_data(cx.db.upcast()).fields().len() as u32; - - (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| { - let ty = field_ty[fid].clone().substitute(Interner, substs); - let ty = normalize(cx.db, cx.db.trait_environment_for_body(cx.body), ty); - let is_visible = matches!(adt, hir_def::AdtId::EnumId(..)) - || visibility[fid].is_visible_from(cx.db.upcast(), cx.module); - let is_uninhabited = cx.is_uninhabited(&ty); - - if is_uninhabited && (!is_visible || is_non_exhaustive) { - None - } else { - Some((fid, ty)) - } - }) - } - - /// Creates a new list of wildcard fields for a given constructor. The result must have a - /// length of `constructor.arity()`. - pub(crate) fn wildcards( - cx: &MatchCheckCtx<'_, 'p>, - ty: &Ty, - constructor: &Constructor, - ) -> Self { - let ret = match constructor { - Single | Variant(_) => match ty.kind(Interner) { - TyKind::Tuple(_, substs) => { - let tys = substs.iter(Interner).map(|ty| ty.assert_ty_ref(Interner)); - Fields::wildcards_from_tys(cx, tys.cloned()) - } - TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())), - &TyKind::Adt(AdtId(adt), ref substs) => { - if is_box(cx.db, adt) { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); - Fields::wildcards_from_tys(cx, once(subst_ty)) - } else { - let variant = constructor.variant_id_for_adt(adt); - let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant) - .map(|(_, ty)| ty); - Fields::wildcards_from_tys(cx, tys) - } - } - ty_kind => { - never!("Unexpected type for `Single` constructor: {:?}", ty_kind); - Fields::wildcards_from_tys(cx, once(ty.clone())) - } - }, - Slice(slice) => match slice._unimplemented {}, - Str(..) - | FloatRange(..) - | IntRange(..) - | NonExhaustive - | Opaque - | Missing { .. } - | Wildcard => Fields::empty(), - Or => { - never!("called `Fields::wildcards` on an `Or` ctor"); - Fields::empty() - } - }; - ret - } - - /// Returns the list of patterns. - pub(super) fn iter_patterns<'a>( - &'a self, - ) -> impl Iterator<Item = &'p DeconstructedPat<'p>> + Captures<'a> { - self.fields.iter() - } -} - -/// Values and patterns can be represented as a constructor applied to some fields. This represents -/// a pattern in this form. -/// This also keeps track of whether the pattern has been found reachable during analysis. For this -/// reason we should be careful not to clone patterns for which we care about that. Use -/// `clone_and_forget_reachability` if you're sure. -pub(crate) struct DeconstructedPat<'p> { - ctor: Constructor, - fields: Fields<'p>, - ty: Ty, - reachable: Cell<bool>, -} - -impl<'p> DeconstructedPat<'p> { - pub(super) fn wildcard(ty: Ty) -> Self { - Self::new(Wildcard, Fields::empty(), ty) - } - - pub(super) fn new(ctor: Constructor, fields: Fields<'p>, ty: Ty) -> Self { - DeconstructedPat { ctor, fields, ty, reachable: Cell::new(false) } - } - - /// Construct a pattern that matches everything that starts with this constructor. - /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern - /// `Some(_)`. - pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p>, ctor: Constructor) -> Self { - let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor); - DeconstructedPat::new(ctor, fields, pcx.ty.clone()) - } - - /// Clone this value. This method emphasizes that cloning loses reachability information and - /// should be done carefully. - pub(super) fn clone_and_forget_reachability(&self) -> Self { - DeconstructedPat::new(self.ctor.clone(), self.fields, self.ty.clone()) - } - - pub(crate) fn from_pat(cx: &MatchCheckCtx<'_, 'p>, pat: &Pat) -> Self { - let mkpat = |pat| DeconstructedPat::from_pat(cx, pat); - let ctor; - let fields; - match pat.kind.as_ref() { - PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat), - PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { - ctor = Wildcard; - fields = Fields::empty(); - } - PatKind::Deref { subpattern } => { - ctor = Single; - fields = Fields::singleton(cx, mkpat(subpattern)); - } - PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { - match pat.ty.kind(Interner) { - TyKind::Tuple(_, substs) => { - ctor = Single; - let mut wilds: SmallVec<[_; 2]> = substs - .iter(Interner) - .map(|arg| arg.assert_ty_ref(Interner).clone()) - .map(DeconstructedPat::wildcard) - .collect(); - for pat in subpatterns { - let idx: u32 = pat.field.into_raw().into(); - wilds[idx as usize] = mkpat(&pat.pattern); - } - fields = Fields::from_iter(cx, wilds) - } - TyKind::Adt(adt, substs) if is_box(cx.db, adt.0) => { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, - // _)` or a box pattern. As a hack to avoid an ICE with the former, we - // ignore other fields than the first one. This will trigger an error later - // anyway. - // See https://github.com/rust-lang/rust/issues/82772 , - // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977 - // The problem is that we can't know from the type whether we'll match - // normally or through box-patterns. We'll have to figure out a proper - // solution when we introduce generalized deref patterns. Also need to - // prevent mixing of those two options. - let pat = - subpatterns.iter().find(|pat| pat.field.into_raw() == 0u32.into()); - let field = if let Some(pat) = pat { - mkpat(&pat.pattern) - } else { - let ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); - DeconstructedPat::wildcard(ty) - }; - ctor = Single; - fields = Fields::singleton(cx, field) - } - &TyKind::Adt(adt, _) => { - ctor = match pat.kind.as_ref() { - PatKind::Leaf { .. } => Single, - PatKind::Variant { enum_variant, .. } => Variant(*enum_variant), - _ => { - never!(); - Wildcard - } - }; - let variant = ctor.variant_id_for_adt(adt.0); - let fields_len = variant.variant_data(cx.db.upcast()).fields().len(); - // 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>> = vec![None; fields_len]; - let tys = Fields::list_variant_nonhidden_fields(cx, &pat.ty, variant) - .enumerate() - .map(|(i, (fid, ty))| { - let field_idx: u32 = fid.into_raw().into(); - field_id_to_id[field_idx as usize] = Some(i); - ty - }); - let mut wilds: SmallVec<[_; 2]> = - tys.map(DeconstructedPat::wildcard).collect(); - for pat in subpatterns { - let field_idx: u32 = pat.field.into_raw().into(); - if let Some(i) = field_id_to_id[field_idx as usize] { - wilds[i] = mkpat(&pat.pattern); - } - } - fields = Fields::from_iter(cx, wilds); - } - _ => { - never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); - ctor = Wildcard; - fields = Fields::empty(); - } - } - } - &PatKind::LiteralBool { value } => { - ctor = IntRange(IntRange::from_bool(value)); - fields = Fields::empty(); - } - PatKind::Or { .. } => { - ctor = Or; - let pats: SmallVec<[_; 2]> = expand_or_pat(pat).into_iter().map(mkpat).collect(); - fields = Fields::from_iter(cx, pats) - } - } - DeconstructedPat::new(ctor, fields, pat.ty.clone()) - } - - pub(crate) fn to_pat(&self, cx: &MatchCheckCtx<'_, 'p>) -> Pat { - let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx)); - let pat = match &self.ctor { - Single | Variant(_) => match self.ty.kind(Interner) { - TyKind::Tuple(..) => PatKind::Leaf { - subpatterns: subpatterns - .zip(0u32..) - .map(|(p, i)| FieldPat { - field: LocalFieldId::from_raw(i.into()), - pattern: p, - }) - .collect(), - }, - TyKind::Adt(adt, _) if is_box(cx.db, adt.0) => { - // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside - // of `std`). So this branch is only reachable when the feature is enabled and - // the pattern is a box pattern. - PatKind::Deref { subpattern: subpatterns.next().unwrap() } - } - TyKind::Adt(adt, substs) => { - let variant = self.ctor.variant_id_for_adt(adt.0); - let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty(), variant) - .zip(subpatterns) - .map(|((field, _ty), pattern)| FieldPat { field, pattern }) - .collect(); - - if let VariantId::EnumVariantId(enum_variant) = variant { - PatKind::Variant { substs: substs.clone(), enum_variant, subpatterns } - } else { - PatKind::Leaf { subpatterns } - } - } - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to reconstruct the correct constant pattern here. However a string - // literal pattern will never be reported as a non-exhaustiveness witness, so we - // ignore this issue. - TyKind::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, - _ => { - never!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty); - PatKind::Wild - } - }, - &Slice(slice) => match slice._unimplemented {}, - &Str(void) => match void {}, - &FloatRange(void) => match void {}, - IntRange(range) => return range.to_pat(cx, self.ty.clone()), - Wildcard | NonExhaustive => PatKind::Wild, - Missing { .. } => { - never!( - "trying to convert a `Missing` constructor into a `Pat`; this is a bug, \ - `Missing` should have been processed in `apply_constructors`" - ); - PatKind::Wild - } - Opaque | Or => { - never!("can't convert to pattern: {:?}", self.ctor); - PatKind::Wild - } - }; - Pat { ty: self.ty.clone(), kind: Box::new(pat) } - } - - pub(super) fn is_or_pat(&self) -> bool { - matches!(self.ctor, Or) - } - - pub(super) fn ctor(&self) -> &Constructor { - &self.ctor - } - - pub(super) fn ty(&self) -> &Ty { - &self.ty - } - - pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'p DeconstructedPat<'p>> + 'a { - self.fields.iter_patterns() - } - - /// Specialize this pattern with a constructor. - /// `other_ctor` can be different from `self.ctor`, but must be covered by it. - pub(super) fn specialize<'a>( - &'a self, - cx: &MatchCheckCtx<'_, 'p>, - other_ctor: &Constructor, - ) -> SmallVec<[&'p DeconstructedPat<'p>; 2]> { - match (&self.ctor, other_ctor) { - (Wildcard, _) => { - // We return a wildcard for each field of `other_ctor`. - Fields::wildcards(cx, &self.ty, other_ctor).iter_patterns().collect() - } - (Slice(self_slice), Slice(other_slice)) - if self_slice.arity() != other_slice.arity() => - { - match self_slice._unimplemented {} - } - _ => self.fields.iter_patterns().collect(), - } - } - - /// We keep track for each pattern if it was ever reachable during the analysis. This is used - /// with `unreachable_spans` to report unreachable subpatterns arising from or patterns. - pub(super) fn set_reachable(&self) { - self.reachable.set(true) - } - pub(super) fn is_reachable(&self) -> bool { - self.reachable.get() - } -} - -fn is_field_list_non_exhaustive(variant_id: VariantId, cx: &MatchCheckCtx<'_, '_>) -> bool { - let attr_def_id = match variant_id { - VariantId::EnumVariantId(id) => id.into(), - VariantId::StructId(id) => id.into(), - VariantId::UnionId(id) => id.into(), - }; - cx.db.attrs(attr_def_id).by_key("non_exhaustive").exists() -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs new file mode 100644 index 00000000000..cd67ca59931 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -0,0 +1,475 @@ +//! Interface with `rustc_pattern_analysis`. + +use std::fmt; + +use hir_def::{DefWithBodyId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; +use rustc_hash::FxHashMap; +use rustc_pattern_analysis::{ + constructor::{Constructor, ConstructorSet, VariantVisibility}, + index::IdxContainer, + Captures, TypeCx, +}; +use smallvec::SmallVec; +use stdx::never; +use typed_arena::Arena; + +use crate::{ + db::HirDatabase, + infer::normalize, + inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from}, + AdtId, Interner, Scalar, Ty, TyExt, TyKind, +}; + +use super::{is_box, FieldPat, Pat, PatKind}; + +use Constructor::*; + +// Re-export r-a-specific versions of all these types. +pub(crate) type DeconstructedPat<'p> = + rustc_pattern_analysis::pat::DeconstructedPat<'p, MatchCheckCtx<'p>>; +pub(crate) type MatchArm<'p> = rustc_pattern_analysis::MatchArm<'p, MatchCheckCtx<'p>>; +pub(crate) type WitnessPat<'p> = rustc_pattern_analysis::pat::WitnessPat<MatchCheckCtx<'p>>; + +/// [Constructor] uses this in unimplemented variants. +/// It allows porting match expressions from upstream algorithm without losing semantics. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum Void {} + +#[derive(Clone)] +pub(crate) struct MatchCheckCtx<'p> { + module: ModuleId, + body: DefWithBodyId, + pub(crate) db: &'p dyn HirDatabase, + pub(crate) pattern_arena: &'p Arena<DeconstructedPat<'p>>, + ty_arena: &'p Arena<Ty>, + exhaustive_patterns: bool, +} + +impl<'p> MatchCheckCtx<'p> { + pub(crate) fn new( + module: ModuleId, + body: DefWithBodyId, + db: &'p dyn HirDatabase, + pattern_arena: &'p Arena<DeconstructedPat<'p>>, + ty_arena: &'p Arena<Ty>, + ) -> Self { + let def_map = db.crate_def_map(module.krate()); + let exhaustive_patterns = def_map.is_unstable_feature_enabled("exhaustive_patterns"); + Self { module, body, db, pattern_arena, exhaustive_patterns, ty_arena } + } + + fn is_uninhabited(&self, ty: &Ty) -> bool { + is_ty_uninhabited_from(ty, self.module, self.db) + } + + /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. + fn is_foreign_non_exhaustive_enum(&self, ty: &Ty) -> bool { + match ty.as_adt() { + Some((adt @ hir_def::AdtId::EnumId(_), _)) => { + let has_non_exhaustive_attr = + self.db.attrs(adt.into()).by_key("non_exhaustive").exists(); + let is_local = adt.module(self.db.upcast()).krate() == self.module.krate(); + has_non_exhaustive_attr && !is_local + } + _ => false, + } + } + + fn variant_id_for_adt(&self, ctor: &Constructor<Self>, adt: hir_def::AdtId) -> VariantId { + match ctor { + &Variant(id) => id.into(), + Struct | UnionField => { + assert!(!matches!(adt, hir_def::AdtId::EnumId(_))); + match adt { + hir_def::AdtId::EnumId(_) => unreachable!(), + hir_def::AdtId::StructId(id) => id.into(), + hir_def::AdtId::UnionId(id) => id.into(), + } + } + _ => panic!("bad constructor {self:?} for adt {adt:?}"), + } + } + + // 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. + fn list_variant_nonhidden_fields<'a>( + &'a self, + ty: &'a Ty, + variant: VariantId, + ) -> impl Iterator<Item = (LocalFieldId, Ty)> + Captures<'a> + Captures<'p> { + let cx = self; + let (adt, substs) = ty.as_adt().unwrap(); + + let adt_is_local = variant.module(cx.db.upcast()).krate() == cx.module.krate(); + + // Whether we must not match the fields of this variant exhaustively. + let is_non_exhaustive = + cx.db.attrs(variant.into()).by_key("non_exhaustive").exists() && !adt_is_local; + + let visibility = cx.db.field_visibilities(variant); + let field_ty = cx.db.field_types(variant); + let fields_len = variant.variant_data(cx.db.upcast()).fields().len() as u32; + + (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| { + let ty = field_ty[fid].clone().substitute(Interner, substs); + let ty = normalize(cx.db, cx.db.trait_environment_for_body(cx.body), ty); + let is_visible = matches!(adt, hir_def::AdtId::EnumId(..)) + || visibility[fid].is_visible_from(cx.db.upcast(), cx.module); + let is_uninhabited = cx.is_uninhabited(&ty); + + if is_uninhabited && (!is_visible || is_non_exhaustive) { + None + } else { + Some((fid, ty)) + } + }) + } + + pub(crate) fn lower_pat(&self, pat: &Pat) -> DeconstructedPat<'p> { + let singleton = |pat| std::slice::from_ref(self.pattern_arena.alloc(pat)); + let ctor; + let fields: &[_]; + + match pat.kind.as_ref() { + PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), + PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { + ctor = Wildcard; + fields = &[]; + } + PatKind::Deref { subpattern } => { + ctor = match pat.ty.kind(Interner) { + // This is a box pattern. + TyKind::Adt(adt, _) if is_box(self.db, adt.0) => Struct, + TyKind::Ref(..) => Ref, + _ => { + never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); + Wildcard + } + }; + fields = singleton(self.lower_pat(subpattern)); + } + PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { + match pat.ty.kind(Interner) { + TyKind::Tuple(_, substs) => { + ctor = Struct; + let mut wilds: SmallVec<[_; 2]> = substs + .iter(Interner) + .map(|arg| arg.assert_ty_ref(Interner).clone()) + .map(DeconstructedPat::wildcard) + .collect(); + for pat in subpatterns { + let idx: u32 = pat.field.into_raw().into(); + wilds[idx as usize] = self.lower_pat(&pat.pattern); + } + fields = self.pattern_arena.alloc_extend(wilds) + } + TyKind::Adt(adt, substs) if is_box(self.db, adt.0) => { + // The only legal patterns of type `Box` (outside `std`) are `_` and box + // patterns. If we're here we can assume this is a box pattern. + // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, + // _)` or a box pattern. As a hack to avoid an ICE with the former, we + // ignore other fields than the first one. This will trigger an error later + // anyway. + // See https://github.com/rust-lang/rust/issues/82772 , + // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977 + // The problem is that we can't know from the type whether we'll match + // normally or through box-patterns. We'll have to figure out a proper + // solution when we introduce generalized deref patterns. Also need to + // prevent mixing of those two options. + let pat = + subpatterns.iter().find(|pat| pat.field.into_raw() == 0u32.into()); + let field = if let Some(pat) = pat { + self.lower_pat(&pat.pattern) + } else { + let ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); + DeconstructedPat::wildcard(ty) + }; + ctor = Struct; + fields = singleton(field); + } + &TyKind::Adt(adt, _) => { + ctor = match pat.kind.as_ref() { + PatKind::Leaf { .. } if matches!(adt.0, hir_def::AdtId::UnionId(_)) => { + UnionField + } + PatKind::Leaf { .. } => Struct, + PatKind::Variant { enum_variant, .. } => Variant(*enum_variant), + _ => { + never!(); + Wildcard + } + }; + let variant = self.variant_id_for_adt(&ctor, adt.0); + let fields_len = variant.variant_data(self.db.upcast()).fields().len(); + // 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>> = vec![None; fields_len]; + let tys = self + .list_variant_nonhidden_fields(&pat.ty, variant) + .enumerate() + .map(|(i, (fid, ty))| { + let field_idx: u32 = fid.into_raw().into(); + field_id_to_id[field_idx as usize] = Some(i); + ty + }); + let mut wilds: SmallVec<[_; 2]> = + tys.map(DeconstructedPat::wildcard).collect(); + for pat in subpatterns { + let field_idx: u32 = pat.field.into_raw().into(); + if let Some(i) = field_id_to_id[field_idx as usize] { + wilds[i] = self.lower_pat(&pat.pattern); + } + } + fields = self.pattern_arena.alloc_extend(wilds); + } + _ => { + never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); + ctor = Wildcard; + fields = &[]; + } + } + } + &PatKind::LiteralBool { value } => { + ctor = Bool(value); + fields = &[]; + } + PatKind::Or { pats } => { + ctor = Or; + // Collect here because `Arena::alloc_extend` panics on reentrancy. + let subpats: SmallVec<[_; 2]> = + pats.into_iter().map(|pat| self.lower_pat(pat)).collect(); + fields = self.pattern_arena.alloc_extend(subpats); + } + } + DeconstructedPat::new(ctor, fields, pat.ty.clone(), ()) + } + + pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'p>) -> Pat { + let mut subpatterns = pat.iter_fields().map(|p| self.hoist_witness_pat(p)); + let kind = match pat.ctor() { + &Bool(value) => PatKind::LiteralBool { value }, + IntRange(_) => unimplemented!(), + Struct | Variant(_) | UnionField => match pat.ty().kind(Interner) { + TyKind::Tuple(..) => PatKind::Leaf { + subpatterns: subpatterns + .zip(0u32..) + .map(|(p, i)| FieldPat { + field: LocalFieldId::from_raw(i.into()), + pattern: p, + }) + .collect(), + }, + TyKind::Adt(adt, _) if is_box(self.db, adt.0) => { + // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside + // of `std`). So this branch is only reachable when the feature is enabled and + // the pattern is a box pattern. + PatKind::Deref { subpattern: subpatterns.next().unwrap() } + } + TyKind::Adt(adt, substs) => { + let variant = self.variant_id_for_adt(pat.ctor(), adt.0); + let subpatterns = self + .list_variant_nonhidden_fields(pat.ty(), variant) + .zip(subpatterns) + .map(|((field, _ty), pattern)| FieldPat { field, pattern }) + .collect(); + + if let VariantId::EnumVariantId(enum_variant) = variant { + PatKind::Variant { substs: substs.clone(), enum_variant, subpatterns } + } else { + PatKind::Leaf { subpatterns } + } + } + _ => { + never!("unexpected ctor for type {:?} {:?}", pat.ctor(), pat.ty()); + PatKind::Wild + } + }, + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to reconstruct the correct constant pattern here. However a string + // literal pattern will never be reported as a non-exhaustiveness witness, so we + // ignore this issue. + Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, + Slice(_) => unimplemented!(), + &Str(void) => match void {}, + Wildcard | NonExhaustive | Hidden => PatKind::Wild, + Missing | F32Range(..) | F64Range(..) | Opaque(..) | Or => { + never!("can't convert to pattern: {:?}", pat.ctor()); + PatKind::Wild + } + }; + Pat { ty: pat.ty().clone(), kind: Box::new(kind) } + } +} + +impl<'p> TypeCx for MatchCheckCtx<'p> { + type Error = Void; + type Ty = Ty; + type VariantIdx = EnumVariantId; + type StrLit = Void; + type ArmData = (); + type PatData = (); + + fn is_exhaustive_patterns_feature_on(&self) -> bool { + self.exhaustive_patterns + } + + fn ctor_arity( + &self, + ctor: &rustc_pattern_analysis::constructor::Constructor<Self>, + ty: &Self::Ty, + ) -> usize { + match ctor { + Struct | Variant(_) | UnionField => match *ty.kind(Interner) { + TyKind::Tuple(arity, ..) => arity, + TyKind::Adt(AdtId(adt), ..) => { + if is_box(self.db, adt) { + // The only legal patterns of type `Box` (outside `std`) are `_` and box + // patterns. If we're here we can assume this is a box pattern. + 1 + } else { + let variant = self.variant_id_for_adt(ctor, adt); + self.list_variant_nonhidden_fields(ty, variant).count() + } + } + _ => { + never!("Unexpected type for `Single` constructor: {:?}", ty); + 0 + } + }, + Ref => 1, + Slice(..) => unimplemented!(), + Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..) + | NonExhaustive | Hidden | Missing | Wildcard => 0, + Or => { + never!("The `Or` constructor doesn't have a fixed arity"); + 0 + } + } + } + + fn ctor_sub_tys( + &self, + ctor: &rustc_pattern_analysis::constructor::Constructor<Self>, + ty: &Self::Ty, + ) -> &[Self::Ty] { + use std::iter::once; + fn alloc<'a>(cx: &'a MatchCheckCtx<'_>, iter: impl Iterator<Item = Ty>) -> &'a [Ty] { + cx.ty_arena.alloc_extend(iter) + } + match ctor { + Struct | Variant(_) | UnionField => match ty.kind(Interner) { + TyKind::Tuple(_, substs) => { + let tys = substs.iter(Interner).map(|ty| ty.assert_ty_ref(Interner)); + alloc(self, tys.cloned()) + } + TyKind::Ref(.., rty) => alloc(self, once(rty.clone())), + &TyKind::Adt(AdtId(adt), ref substs) => { + if is_box(self.db, adt) { + // The only legal patterns of type `Box` (outside `std`) are `_` and box + // patterns. If we're here we can assume this is a box pattern. + let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); + alloc(self, once(subst_ty)) + } else { + let variant = self.variant_id_for_adt(ctor, adt); + let tys = self.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty); + alloc(self, tys) + } + } + ty_kind => { + never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind); + alloc(self, once(ty.clone())) + } + }, + Ref => match ty.kind(Interner) { + TyKind::Ref(.., rty) => alloc(self, once(rty.clone())), + ty_kind => { + never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind); + alloc(self, once(ty.clone())) + } + }, + Slice(_) => unreachable!("Found a `Slice` constructor in match checking"), + Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..) + | NonExhaustive | Hidden | Missing | Wildcard => &[], + Or => { + never!("called `Fields::wildcards` on an `Or` ctor"); + &[] + } + } + } + + fn ctors_for_ty( + &self, + ty: &Self::Ty, + ) -> Result<rustc_pattern_analysis::constructor::ConstructorSet<Self>, Self::Error> { + let cx = self; + + // Unhandled types are treated as non-exhaustive. Being explicit here instead of falling + // to catchall arm to ease further implementation. + let unhandled = || ConstructorSet::Unlistable; + + // This determines the set of all possible constructors for the type `ty`. For numbers, + // arrays and slices we use ranges and variable-length slices when appropriate. + // + // If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that + // are statically impossible. E.g., for `Option<!>`, we do not include `Some(_)` in the + // returned list of constructors. + // Invariant: this is empty if and only if the type is uninhabited (as determined by + // `cx.is_uninhabited()`). + Ok(match ty.kind(Interner) { + TyKind::Scalar(Scalar::Bool) => ConstructorSet::Bool, + TyKind::Scalar(Scalar::Char) => unhandled(), + TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(), + TyKind::Array(..) | TyKind::Slice(..) => unhandled(), + TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), subst) => { + let enum_data = cx.db.enum_data(*enum_id); + let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty); + + if enum_data.variants.is_empty() && !is_declared_nonexhaustive { + ConstructorSet::NoConstructors + } else { + let mut variants = FxHashMap::default(); + for &(variant, _) in enum_data.variants.iter() { + let is_uninhabited = + is_enum_variant_uninhabited_from(variant, subst, cx.module, cx.db); + let visibility = if is_uninhabited { + VariantVisibility::Empty + } else { + VariantVisibility::Visible + }; + variants.insert(variant, visibility); + } + + ConstructorSet::Variants { + variants: IdxContainer(variants), + non_exhaustive: is_declared_nonexhaustive, + } + } + } + TyKind::Adt(AdtId(hir_def::AdtId::UnionId(_)), _) => ConstructorSet::Union, + TyKind::Adt(..) | TyKind::Tuple(..) => { + ConstructorSet::Struct { empty: cx.is_uninhabited(ty) } + } + TyKind::Ref(..) => ConstructorSet::Ref, + TyKind::Never => ConstructorSet::NoConstructors, + // This type is one for which we cannot list constructors, like `str` or `f64`. + _ => ConstructorSet::Unlistable, + }) + } + + fn debug_pat( + _f: &mut fmt::Formatter<'_>, + _pat: &rustc_pattern_analysis::pat::DeconstructedPat<'_, Self>, + ) -> fmt::Result { + unimplemented!() + } + + fn bug(&self, fmt: fmt::Arguments<'_>) -> ! { + panic!("{}", fmt) + } +} + +impl<'p> fmt::Debug for MatchCheckCtx<'p> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MatchCheckCtx").finish() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs deleted file mode 100644 index 1b1a5ff2694..00000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs +++ /dev/null @@ -1,824 +0,0 @@ -//! Based on rust-lang/rust (last sync f31622a50 2021-11-12) -//! <https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs> -//! -//! ----- -//! -//! This file includes the logic for exhaustiveness and reachability checking for pattern-matching. -//! Specifically, given a list of patterns for a type, we can tell whether: -//! (a) each pattern is reachable (reachability) -//! (b) the patterns cover every possible value for the type (exhaustiveness) -//! -//! The algorithm implemented here is a modified version of the one described in [this -//! paper](http://moscova.inria.fr/~maranget/papers/warn/index.html). We have however generalized -//! it to accommodate the variety of patterns that Rust supports. We thus explain our version here, -//! without being as rigorous. -//! -//! -//! # Summary -//! -//! The core of the algorithm is the notion of "usefulness". A pattern `q` is said to be *useful* -//! relative to another pattern `p` of the same type if there is a value that is matched by `q` and -//! not matched by `p`. This generalizes to many `p`s: `q` is useful w.r.t. a list of patterns -//! `p_1 .. p_n` if there is a value that is matched by `q` and by none of the `p_i`. We write -//! `usefulness(p_1 .. p_n, q)` for a function that returns a list of such values. The aim of this -//! file is to compute it efficiently. -//! -//! This is enough to compute reachability: a pattern in a `match` expression is reachable iff it -//! is useful w.r.t. the patterns above it: -//! ```rust -//! match x { -//! Some(_) => ..., -//! None => ..., // reachable: `None` is matched by this but not the branch above -//! Some(0) => ..., // unreachable: all the values this matches are already matched by -//! // `Some(_)` above -//! } -//! ``` -//! -//! This is also enough to compute exhaustiveness: a match is exhaustive iff the wildcard `_` -//! pattern is _not_ useful w.r.t. the patterns in the match. The values returned by `usefulness` -//! are used to tell the user which values are missing. -//! ```rust -//! match x { -//! Some(0) => ..., -//! None => ..., -//! // not exhaustive: `_` is useful because it matches `Some(1)` -//! } -//! ``` -//! -//! The entrypoint of this file is the [`compute_match_usefulness`] function, which computes -//! reachability for each match branch and exhaustiveness for the whole match. -//! -//! -//! # Constructors and fields -//! -//! Note: we will often abbreviate "constructor" as "ctor". -//! -//! The idea that powers everything that is done in this file is the following: a (matcheable) -//! value is made from a constructor applied to a number of subvalues. Examples of constructors are -//! `Some`, `None`, `(,)` (the 2-tuple constructor), `Foo {..}` (the constructor for a struct -//! `Foo`), and `2` (the constructor for the number `2`). This is natural when we think of -//! pattern-matching, and this is the basis for what follows. -//! -//! Some of the ctors listed above might feel weird: `None` and `2` don't take any arguments. -//! That's ok: those are ctors that take a list of 0 arguments; they are the simplest case of -//! ctors. We treat `2` as a ctor because `u64` and other number types behave exactly like a huge -//! `enum`, with one variant for each number. This allows us to see any matcheable value as made up -//! from a tree of ctors, each having a set number of children. For example: `Foo { bar: None, -//! baz: Ok(0) }` is made from 4 different ctors, namely `Foo{..}`, `None`, `Ok` and `0`. -//! -//! This idea can be extended to patterns: they are also made from constructors applied to fields. -//! A pattern for a given type is allowed to use all the ctors for values of that type (which we -//! call "value constructors"), but there are also pattern-only ctors. The most important one is -//! the wildcard (`_`), and the others are integer ranges (`0..=10`), variable-length slices (`[x, -//! ..]`), and or-patterns (`Ok(0) | Err(_)`). Examples of valid patterns are `42`, `Some(_)`, `Foo -//! { bar: Some(0) | None, baz: _ }`. Note that a binder in a pattern (e.g. `Some(x)`) matches the -//! same values as a wildcard (e.g. `Some(_)`), so we treat both as wildcards. -//! -//! From this deconstruction we can compute whether a given value matches a given pattern; we -//! simply look at ctors one at a time. Given a pattern `p` and a value `v`, we want to compute -//! `matches!(v, p)`. It's mostly straightforward: we compare the head ctors and when they match -//! we compare their fields recursively. A few representative examples: -//! -//! - `matches!(v, _) := true` -//! - `matches!((v0, v1), (p0, p1)) := matches!(v0, p0) && matches!(v1, p1)` -//! - `matches!(Foo { bar: v0, baz: v1 }, Foo { bar: p0, baz: p1 }) := matches!(v0, p0) && matches!(v1, p1)` -//! - `matches!(Ok(v0), Ok(p0)) := matches!(v0, p0)` -//! - `matches!(Ok(v0), Err(p0)) := false` (incompatible variants) -//! - `matches!(v, 1..=100) := matches!(v, 1) || ... || matches!(v, 100)` -//! - `matches!([v0], [p0, .., p1]) := false` (incompatible lengths) -//! - `matches!([v0, v1, v2], [p0, .., p1]) := matches!(v0, p0) && matches!(v2, p1)` -//! - `matches!(v, p0 | p1) := matches!(v, p0) || matches!(v, p1)` -//! -//! Constructors, fields and relevant operations are defined in the [`super::deconstruct_pat`] module. -//! -//! Note: this constructors/fields distinction may not straightforwardly apply to every Rust type. -//! For example a value of type `Rc<u64>` can't be deconstructed that way, and `&str` has an -//! infinitude of constructors. There are also subtleties with visibility of fields and -//! uninhabitedness and various other things. The constructors idea can be extended to handle most -//! of these subtleties though; caveats are documented where relevant throughout the code. -//! -//! Whether constructors cover each other is computed by [`Constructor::is_covered_by`]. -//! -//! -//! # Specialization -//! -//! Recall that we wish to compute `usefulness(p_1 .. p_n, q)`: given a list of patterns `p_1 .. -//! p_n` and a pattern `q`, all of the same type, we want to find a list of values (called -//! "witnesses") that are matched by `q` and by none of the `p_i`. We obviously don't just -//! enumerate all possible values. From the discussion above we see that we can proceed -//! ctor-by-ctor: for each value ctor of the given type, we ask "is there a value that starts with -//! this constructor and matches `q` and none of the `p_i`?". As we saw above, there's a lot we can -//! say from knowing only the first constructor of our candidate value. -//! -//! Let's take the following example: -//! ``` -//! match x { -//! Enum::Variant1(_) => {} // `p1` -//! Enum::Variant2(None, 0) => {} // `p2` -//! Enum::Variant2(Some(_), 0) => {} // `q` -//! } -//! ``` -//! -//! We can easily see that if our candidate value `v` starts with `Variant1` it will not match `q`. -//! If `v = Variant2(v0, v1)` however, whether or not it matches `p2` and `q` will depend on `v0` -//! and `v1`. In fact, such a `v` will be a witness of usefulness of `q` exactly when the tuple -//! `(v0, v1)` is a witness of usefulness of `q'` in the following reduced match: -//! -//! ``` -//! match x { -//! (None, 0) => {} // `p2'` -//! (Some(_), 0) => {} // `q'` -//! } -//! ``` -//! -//! This motivates a new step in computing usefulness, that we call _specialization_. -//! Specialization consist of filtering a list of patterns for those that match a constructor, and -//! then looking into the constructor's fields. This enables usefulness to be computed recursively. -//! -//! Instead of acting on a single pattern in each row, we will consider a list of patterns for each -//! row, and we call such a list a _pattern-stack_. The idea is that we will specialize the -//! leftmost pattern, which amounts to popping the constructor and pushing its fields, which feels -//! like a stack. We note a pattern-stack simply with `[p_1 ... p_n]`. -//! Here's a sequence of specializations of a list of pattern-stacks, to illustrate what's -//! happening: -//! ``` -//! [Enum::Variant1(_)] -//! [Enum::Variant2(None, 0)] -//! [Enum::Variant2(Some(_), 0)] -//! //==>> specialize with `Variant2` -//! [None, 0] -//! [Some(_), 0] -//! //==>> specialize with `Some` -//! [_, 0] -//! //==>> specialize with `true` (say the type was `bool`) -//! [0] -//! //==>> specialize with `0` -//! [] -//! ``` -//! -//! The function `specialize(c, p)` takes a value constructor `c` and a pattern `p`, and returns 0 -//! or more pattern-stacks. If `c` does not match the head constructor of `p`, it returns nothing; -//! otherwise if returns the fields of the constructor. This only returns more than one -//! pattern-stack if `p` has a pattern-only constructor. -//! -//! - Specializing for the wrong constructor returns nothing -//! -//! `specialize(None, Some(p0)) := []` -//! -//! - Specializing for the correct constructor returns a single row with the fields -//! -//! `specialize(Variant1, Variant1(p0, p1, p2)) := [[p0, p1, p2]]` -//! -//! `specialize(Foo{..}, Foo { bar: p0, baz: p1 }) := [[p0, p1]]` -//! -//! - For or-patterns, we specialize each branch and concatenate the results -//! -//! `specialize(c, p0 | p1) := specialize(c, p0) ++ specialize(c, p1)` -//! -//! - We treat the other pattern constructors as if they were a large or-pattern of all the -//! possibilities: -//! -//! `specialize(c, _) := specialize(c, Variant1(_) | Variant2(_, _) | ...)` -//! -//! `specialize(c, 1..=100) := specialize(c, 1 | ... | 100)` -//! -//! `specialize(c, [p0, .., p1]) := specialize(c, [p0, p1] | [p0, _, p1] | [p0, _, _, p1] | ...)` -//! -//! - If `c` is a pattern-only constructor, `specialize` is defined on a case-by-case basis. See -//! the discussion about constructor splitting in [`super::deconstruct_pat`]. -//! -//! -//! We then extend this function to work with pattern-stacks as input, by acting on the first -//! column and keeping the other columns untouched. -//! -//! Specialization for the whole matrix is done in [`Matrix::specialize_constructor`]. Note that -//! or-patterns in the first column are expanded before being stored in the matrix. Specialization -//! for a single patstack is done from a combination of [`Constructor::is_covered_by`] and -//! [`PatStack::pop_head_constructor`]. The internals of how it's done mostly live in the -//! [`Fields`] struct. -//! -//! -//! # Computing usefulness -//! -//! We now have all we need to compute usefulness. The inputs to usefulness are a list of -//! pattern-stacks `p_1 ... p_n` (one per row), and a new pattern_stack `q`. The paper and this -//! file calls the list of patstacks a _matrix_. They must all have the same number of columns and -//! the patterns in a given column must all have the same type. `usefulness` returns a (possibly -//! empty) list of witnesses of usefulness. These witnesses will also be pattern-stacks. -//! -//! - base case: `n_columns == 0`. -//! Since a pattern-stack functions like a tuple of patterns, an empty one functions like the -//! unit type. Thus `q` is useful iff there are no rows above it, i.e. if `n == 0`. -//! -//! - inductive case: `n_columns > 0`. -//! We need a way to list the constructors we want to try. We will be more clever in the next -//! section but for now assume we list all value constructors for the type of the first column. -//! -//! - for each such ctor `c`: -//! -//! - for each `q'` returned by `specialize(c, q)`: -//! -//! - we compute `usefulness(specialize(c, p_1) ... specialize(c, p_n), q')` -//! -//! - for each witness found, we revert specialization by pushing the constructor `c` on top. -//! -//! - We return the concatenation of all the witnesses found, if any. -//! -//! Example: -//! ``` -//! [Some(true)] // p_1 -//! [None] // p_2 -//! [Some(_)] // q -//! //==>> try `None`: `specialize(None, q)` returns nothing -//! //==>> try `Some`: `specialize(Some, q)` returns a single row -//! [true] // p_1' -//! [_] // q' -//! //==>> try `true`: `specialize(true, q')` returns a single row -//! [] // p_1'' -//! [] // q'' -//! //==>> base case; `n != 0` so `q''` is not useful. -//! //==>> go back up a step -//! [true] // p_1' -//! [_] // q' -//! //==>> try `false`: `specialize(false, q')` returns a single row -//! [] // q'' -//! //==>> base case; `n == 0` so `q''` is useful. We return the single witness `[]` -//! witnesses: -//! [] -//! //==>> undo the specialization with `false` -//! witnesses: -//! [false] -//! //==>> undo the specialization with `Some` -//! witnesses: -//! [Some(false)] -//! //==>> we have tried all the constructors. The output is the single witness `[Some(false)]`. -//! ``` -//! -//! This computation is done in [`is_useful`]. In practice we don't care about the list of -//! witnesses when computing reachability; we only need to know whether any exist. We do keep the -//! witnesses when computing exhaustiveness to report them to the user. -//! -//! -//! # Making usefulness tractable: constructor splitting -//! -//! We're missing one last detail: which constructors do we list? Naively listing all value -//! constructors cannot work for types like `u64` or `&str`, so we need to be more clever. The -//! first obvious insight is that we only want to list constructors that are covered by the head -//! constructor of `q`. If it's a value constructor, we only try that one. If it's a pattern-only -//! constructor, we use the final clever idea for this algorithm: _constructor splitting_, where we -//! group together constructors that behave the same. -//! -//! The details are not necessary to understand this file, so we explain them in -//! [`super::deconstruct_pat`]. Splitting is done by the [`Constructor::split`] function. - -use std::iter::once; - -use hir_def::{AdtId, DefWithBodyId, HasModule, ModuleId}; -use smallvec::{smallvec, SmallVec}; -use typed_arena::Arena; - -use crate::{db::HirDatabase, inhabitedness::is_ty_uninhabited_from, Ty, TyExt}; - -use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard}; - -use self::{helper::Captures, ArmType::*, Usefulness::*}; - -pub(crate) struct MatchCheckCtx<'a, 'p> { - pub(crate) module: ModuleId, - pub(crate) body: DefWithBodyId, - pub(crate) db: &'a dyn HirDatabase, - /// Lowered patterns from arms plus generated by the check. - pub(crate) pattern_arena: &'p Arena<DeconstructedPat<'p>>, - exhaustive_patterns: bool, -} - -impl<'a, 'p> MatchCheckCtx<'a, 'p> { - pub(crate) fn new( - module: ModuleId, - body: DefWithBodyId, - db: &'a dyn HirDatabase, - pattern_arena: &'p Arena<DeconstructedPat<'p>>, - ) -> Self { - let def_map = db.crate_def_map(module.krate()); - let exhaustive_patterns = def_map.is_unstable_feature_enabled("exhaustive_patterns"); - Self { module, body, db, pattern_arena, exhaustive_patterns } - } - - pub(super) fn is_uninhabited(&self, ty: &Ty) -> bool { - if self.feature_exhaustive_patterns() { - is_ty_uninhabited_from(ty, self.module, self.db) - } else { - false - } - } - - /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. - pub(super) fn is_foreign_non_exhaustive_enum(&self, ty: &Ty) -> bool { - match ty.as_adt() { - Some((adt @ AdtId::EnumId(_), _)) => { - let has_non_exhaustive_attr = - self.db.attrs(adt.into()).by_key("non_exhaustive").exists(); - let is_local = adt.module(self.db.upcast()).krate() == self.module.krate(); - has_non_exhaustive_attr && !is_local - } - _ => false, - } - } - - // Rust's unstable feature described as "Allows exhaustive pattern matching on types that contain uninhabited types." - pub(super) fn feature_exhaustive_patterns(&self) -> bool { - self.exhaustive_patterns - } -} - -#[derive(Copy, Clone)] -pub(super) struct PatCtxt<'a, 'p> { - pub(super) cx: &'a MatchCheckCtx<'a, 'p>, - /// Type of the current column under investigation. - pub(super) ty: &'a Ty, - /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a - /// subpattern. - pub(super) is_top_level: bool, - /// Whether the current pattern is from a `non_exhaustive` enum. - pub(super) is_non_exhaustive: bool, -} - -/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` -/// works well. -#[derive(Clone)] -pub(super) struct PatStack<'p> { - pats: SmallVec<[&'p DeconstructedPat<'p>; 2]>, -} - -impl<'p> PatStack<'p> { - fn from_pattern(pat: &'p DeconstructedPat<'p>) -> Self { - Self::from_vec(smallvec![pat]) - } - - fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p>; 2]>) -> Self { - PatStack { pats: vec } - } - - fn is_empty(&self) -> bool { - self.pats.is_empty() - } - - fn len(&self) -> usize { - self.pats.len() - } - - fn head(&self) -> &'p DeconstructedPat<'p> { - self.pats[0] - } - - // Recursively expand the first pattern into its subpatterns. Only useful if the pattern is an - // or-pattern. Panics if `self` is empty. - fn expand_or_pat(&self) -> impl Iterator<Item = PatStack<'p>> + Captures<'_> { - self.head().iter_fields().map(move |pat| { - let mut new_patstack = PatStack::from_pattern(pat); - new_patstack.pats.extend_from_slice(&self.pats[1..]); - new_patstack - }) - } - - /// This computes `S(self.head().ctor(), self)`. See top of the file for explanations. - /// - /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing - /// fields filled with wild patterns. - /// - /// This is roughly the inverse of `Constructor::apply`. - fn pop_head_constructor(&self, cx: &MatchCheckCtx<'_, 'p>, ctor: &Constructor) -> PatStack<'p> { - // We pop the head pattern and push the new fields extracted from the arguments of - // `self.head()`. - let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor); - new_fields.extend_from_slice(&self.pats[1..]); - PatStack::from_vec(new_fields) - } -} - -/// A 2D matrix. -#[derive(Clone)] -pub(super) struct Matrix<'p> { - patterns: Vec<PatStack<'p>>, -} - -impl<'p> Matrix<'p> { - fn empty() -> Self { - Matrix { patterns: vec![] } - } - - /// Number of columns of this matrix. `None` is the matrix is empty. - pub(super) fn _column_count(&self) -> Option<usize> { - self.patterns.first().map(|r| r.len()) - } - - /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively - /// expands it. - fn push(&mut self, row: PatStack<'p>) { - if !row.is_empty() && row.head().is_or_pat() { - self.patterns.extend(row.expand_or_pat()); - } else { - self.patterns.push(row); - } - } - - /// Iterate over the first component of each row - fn heads(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p>> + Clone + Captures<'_> { - self.patterns.iter().map(|r| r.head()) - } - - /// This computes `S(constructor, self)`. See top of the file for explanations. - fn specialize_constructor(&self, pcx: PatCtxt<'_, 'p>, ctor: &Constructor) -> Matrix<'p> { - let mut matrix = Matrix::empty(); - for row in &self.patterns { - if ctor.is_covered_by(pcx, row.head().ctor()) { - let new_row = row.pop_head_constructor(pcx.cx, ctor); - matrix.push(new_row); - } - } - matrix - } -} - -/// This carries the results of computing usefulness, as described at the top of the file. When -/// checking usefulness of a match branch, we use the `NoWitnesses` variant, which also keeps track -/// of potential unreachable sub-patterns (in the presence of or-patterns). When checking -/// exhaustiveness of a whole match, we use the `WithWitnesses` variant, which carries a list of -/// witnesses of non-exhaustiveness when there are any. -/// Which variant to use is dictated by `ArmType`. -enum Usefulness<'p> { - /// If we don't care about witnesses, simply remember if the pattern was useful. - NoWitnesses { useful: bool }, - /// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole - /// pattern is unreachable. - WithWitnesses(Vec<Witness<'p>>), -} - -impl<'p> Usefulness<'p> { - fn new_useful(preference: ArmType) -> Self { - match preference { - // A single (empty) witness of reachability. - FakeExtraWildcard => WithWitnesses(vec![Witness(vec![])]), - RealArm => NoWitnesses { useful: true }, - } - } - fn new_not_useful(preference: ArmType) -> Self { - match preference { - FakeExtraWildcard => WithWitnesses(vec![]), - RealArm => NoWitnesses { useful: false }, - } - } - - fn is_useful(&self) -> bool { - match self { - Usefulness::NoWitnesses { useful } => *useful, - Usefulness::WithWitnesses(witnesses) => !witnesses.is_empty(), - } - } - - /// Combine usefulnesses from two branches. This is an associative operation. - fn extend(&mut self, other: Self) { - match (&mut *self, other) { - (WithWitnesses(_), WithWitnesses(o)) if o.is_empty() => {} - (WithWitnesses(s), WithWitnesses(o)) if s.is_empty() => *self = WithWitnesses(o), - (WithWitnesses(s), WithWitnesses(o)) => s.extend(o), - (NoWitnesses { useful: s_useful }, NoWitnesses { useful: o_useful }) => { - *s_useful = *s_useful || o_useful - } - _ => unreachable!(), - } - } - - /// After calculating usefulness after a specialization, call this to reconstruct a usefulness - /// that makes sense for the matrix pre-specialization. This new usefulness can then be merged - /// with the results of specializing with the other constructors. - fn apply_constructor( - self, - pcx: PatCtxt<'_, 'p>, - matrix: &Matrix<'p>, - ctor: &Constructor, - ) -> Self { - match self { - NoWitnesses { .. } => self, - WithWitnesses(ref witnesses) if witnesses.is_empty() => self, - WithWitnesses(witnesses) => { - let new_witnesses = if let Constructor::Missing { .. } = ctor { - // We got the special `Missing` constructor, so each of the missing constructors - // gives a new pattern that is not caught by the match. We list those patterns. - let new_patterns = if pcx.is_non_exhaustive { - // Here we don't want the user to try to list all variants, we want them to add - // a wildcard, so we only suggest that. - vec![DeconstructedPat::wildcard(pcx.ty.clone())] - } else { - let mut split_wildcard = SplitWildcard::new(pcx); - split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); - - // This lets us know if we skipped any variants because they are marked - // `doc(hidden)` or they are unstable feature gate (only stdlib types). - let mut hide_variant_show_wild = false; - // Construct for each missing constructor a "wild" version of this - // constructor, that matches everything that can be built with - // it. For example, if `ctor` is a `Constructor::Variant` for - // `Option::Some`, we get the pattern `Some(_)`. - let mut new: Vec<DeconstructedPat<'_>> = split_wildcard - .iter_missing(pcx) - .filter_map(|missing_ctor| { - // Check if this variant is marked `doc(hidden)` - if missing_ctor.is_doc_hidden_variant(pcx) - || missing_ctor.is_unstable_variant(pcx) - { - hide_variant_show_wild = true; - return None; - } - Some(DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone())) - }) - .collect(); - - if hide_variant_show_wild { - new.push(DeconstructedPat::wildcard(pcx.ty.clone())) - } - - new - }; - - witnesses - .into_iter() - .flat_map(|witness| { - new_patterns.iter().map(move |pat| { - Witness( - witness - .0 - .iter() - .chain(once(pat)) - .map(DeconstructedPat::clone_and_forget_reachability) - .collect(), - ) - }) - }) - .collect() - } else { - witnesses - .into_iter() - .map(|witness| witness.apply_constructor(pcx, ctor)) - .collect() - }; - WithWitnesses(new_witnesses) - } - } - } -} - -#[derive(Copy, Clone, Debug)] -enum ArmType { - FakeExtraWildcard, - RealArm, -} - -/// A witness of non-exhaustiveness for error reporting, represented -/// as a list of patterns (in reverse order of construction) with -/// wildcards inside to represent elements that can take any inhabitant -/// of the type as a value. -/// -/// A witness against a list of patterns should have the same types -/// and length as the pattern matched against. Because Rust `match` -/// is always against a single pattern, at the end the witness will -/// have length 1, but in the middle of the algorithm, it can contain -/// multiple patterns. -/// -/// For example, if we are constructing a witness for the match against -/// -/// ``` -/// struct Pair(Option<(u32, u32)>, bool); -/// -/// match (p: Pair) { -/// Pair(None, _) => {} -/// Pair(_, false) => {} -/// } -/// ``` -/// -/// We'll perform the following steps: -/// 1. Start with an empty witness -/// `Witness(vec![])` -/// 2. Push a witness `true` against the `false` -/// `Witness(vec![true])` -/// 3. Push a witness `Some(_)` against the `None` -/// `Witness(vec![true, Some(_)])` -/// 4. Apply the `Pair` constructor to the witnesses -/// `Witness(vec![Pair(Some(_), true)])` -/// -/// The final `Pair(Some(_), true)` is then the resulting witness. -pub(crate) struct Witness<'p>(Vec<DeconstructedPat<'p>>); - -impl<'p> Witness<'p> { - /// Asserts that the witness contains a single pattern, and returns it. - fn single_pattern(self) -> DeconstructedPat<'p> { - assert_eq!(self.0.len(), 1); - self.0.into_iter().next().unwrap() - } - - /// Constructs a partial witness for a pattern given a list of - /// patterns expanded by the specialization step. - /// - /// When a pattern P is discovered to be useful, this function is used bottom-up - /// to reconstruct a complete witness, e.g., a pattern P' that covers a subset - /// of values, V, where each value in that set is not covered by any previously - /// used patterns and is covered by the pattern P'. Examples: - /// - /// left_ty: tuple of 3 elements - /// pats: [10, 20, _] => (10, 20, _) - /// - /// left_ty: struct X { a: (bool, &'static str), b: usize} - /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } - fn apply_constructor(mut self, pcx: PatCtxt<'_, 'p>, ctor: &Constructor) -> Self { - let pat = { - let len = self.0.len(); - let arity = ctor.arity(pcx); - let pats = self.0.drain((len - arity)..).rev(); - let fields = Fields::from_iter(pcx.cx, pats); - DeconstructedPat::new(ctor.clone(), fields, pcx.ty.clone()) - }; - - self.0.push(pat); - - self - } -} - -/// Algorithm from <http://moscova.inria.fr/~maranget/papers/warn/index.html>. -/// The algorithm from the paper has been modified to correctly handle empty -/// types. The changes are: -/// (0) We don't exit early if the pattern matrix has zero rows. We just -/// continue to recurse over columns. -/// (1) all_constructors will only return constructors that are statically -/// possible. E.g., it will only return `Ok` for `Result<T, !>`. -/// -/// This finds whether a (row) vector `v` of patterns is 'useful' in relation -/// to a set of such vectors `m` - this is defined as there being a set of -/// inputs that will match `v` but not any of the sets in `m`. -/// -/// All the patterns at each column of the `matrix ++ v` matrix must have the same type. -/// -/// This is used both for reachability checking (if a pattern isn't useful in -/// relation to preceding patterns, it is not reachable) and exhaustiveness -/// checking (if a wildcard pattern is useful in relation to a matrix, the -/// matrix isn't exhaustive). -/// -/// `is_under_guard` is used to inform if the pattern has a guard. If it -/// has one it must not be inserted into the matrix. This shouldn't be -/// relied on for soundness. -fn is_useful<'p>( - cx: &MatchCheckCtx<'_, 'p>, - matrix: &Matrix<'p>, - v: &PatStack<'p>, - witness_preference: ArmType, - is_under_guard: bool, - is_top_level: bool, -) -> Usefulness<'p> { - let Matrix { patterns: rows, .. } = matrix; - - // The base case. We are pattern-matching on () and the return value is - // based on whether our matrix has a row or not. - // NOTE: This could potentially be optimized by checking rows.is_empty() - // first and then, if v is non-empty, the return value is based on whether - // the type of the tuple we're checking is inhabited or not. - if v.is_empty() { - let ret = if rows.is_empty() { - Usefulness::new_useful(witness_preference) - } else { - Usefulness::new_not_useful(witness_preference) - }; - return ret; - } - - debug_assert!(rows.iter().all(|r| r.len() == v.len())); - - let ty = v.head().ty(); - let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); - let pcx = PatCtxt { cx, ty, is_top_level, is_non_exhaustive }; - - // If the first pattern is an or-pattern, expand it. - let mut ret = Usefulness::new_not_useful(witness_preference); - if v.head().is_or_pat() { - // We try each or-pattern branch in turn. - let mut matrix = matrix.clone(); - for v in v.expand_or_pat() { - let usefulness = is_useful(cx, &matrix, &v, witness_preference, is_under_guard, false); - ret.extend(usefulness); - // If pattern has a guard don't add it to the matrix. - if !is_under_guard { - // We push the already-seen patterns into the matrix in order to detect redundant - // branches like `Some(_) | Some(0)`. - matrix.push(v); - } - } - } else { - let v_ctor = v.head().ctor(); - - // FIXME: implement `overlapping_range_endpoints` lint - - // We split the head constructor of `v`. - let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); - // For each constructor, we compute whether there's a value that starts with it that would - // witness the usefulness of `v`. - let start_matrix = matrix; - for ctor in split_ctors { - // We cache the result of `Fields::wildcards` because it is used a lot. - let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor); - let v = v.pop_head_constructor(cx, &ctor); - let usefulness = - is_useful(cx, &spec_matrix, &v, witness_preference, is_under_guard, false); - let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor); - - // FIXME: implement `non_exhaustive_omitted_patterns` lint - - ret.extend(usefulness); - } - }; - - if ret.is_useful() { - v.head().set_reachable(); - } - - ret -} - -/// The arm of a match expression. -#[derive(Clone, Copy)] -pub(crate) struct MatchArm<'p> { - pub(crate) pat: &'p DeconstructedPat<'p>, - pub(crate) has_guard: bool, -} - -/// Indicates whether or not a given arm is reachable. -#[derive(Clone, Debug)] -pub(crate) enum Reachability { - /// The arm is reachable. This additionally carries a set of or-pattern branches that have been - /// found to be unreachable despite the overall arm being reachable. Used only in the presence - /// of or-patterns, otherwise it stays empty. - // FIXME: store unreachable subpattern IDs - Reachable, - /// The arm is unreachable. - Unreachable, -} - -/// The output of checking a match for exhaustiveness and arm reachability. -pub(crate) struct UsefulnessReport<'p> { - /// For each arm of the input, whether that arm is reachable after the arms above it. - pub(crate) _arm_usefulness: Vec<(MatchArm<'p>, Reachability)>, - /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of - /// exhaustiveness. - pub(crate) non_exhaustiveness_witnesses: Vec<DeconstructedPat<'p>>, -} - -/// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which -/// of its arms are reachable. -/// -/// Note: the input patterns must have been lowered through -/// `check_match::MatchVisitor::lower_pattern`. -pub(crate) fn compute_match_usefulness<'p>( - cx: &MatchCheckCtx<'_, 'p>, - arms: &[MatchArm<'p>], - scrut_ty: &Ty, -) -> UsefulnessReport<'p> { - let mut matrix = Matrix::empty(); - let arm_usefulness = arms - .iter() - .copied() - .map(|arm| { - let v = PatStack::from_pattern(arm.pat); - is_useful(cx, &matrix, &v, RealArm, arm.has_guard, true); - if !arm.has_guard { - matrix.push(v); - } - let reachability = if arm.pat.is_reachable() { - Reachability::Reachable - } else { - Reachability::Unreachable - }; - (arm, reachability) - }) - .collect(); - - let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty.clone())); - let v = PatStack::from_pattern(wild_pattern); - let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, false, true); - let non_exhaustiveness_witnesses = match usefulness { - WithWitnesses(pats) => pats.into_iter().map(Witness::single_pattern).collect(), - NoWitnesses { .. } => panic!("bug"), - }; - UsefulnessReport { _arm_usefulness: arm_usefulness, non_exhaustiveness_witnesses } -} - -pub(crate) mod helper { - // Copy-pasted from rust/compiler/rustc_data_structures/src/captures.rs - /// "Signaling" trait used in impl trait to tag lifetimes that you may - /// need to capture but don't really need for other reasons. - /// Basically a workaround; see [this comment] for details. - /// - /// [this comment]: https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999 - // FIXME(eddyb) false positive, the lifetime parameter is "phantom" but needed. - #[allow(unused_lifetimes)] - pub(crate) trait Captures<'a> {} - - impl<'a, T: ?Sized> Captures<'a> for T {} -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 19052a18b19..54e91e7b29a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -15,6 +15,9 @@ extern crate rustc_abi; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_abi as rustc_abi; +// No need to use the in-tree one. +extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis; + mod builder; mod chalk_db; mod chalk_ext; @@ -39,9 +42,9 @@ pub mod primitive; pub mod traits; #[cfg(test)] -mod tests; -#[cfg(test)] mod test_db; +#[cfg(test)] +mod tests; use std::{ collections::hash_map::Entry, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 952a97e3d0f..494f1850b88 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -21,11 +21,11 @@ use hir_def::{ }; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; +mod borrowck; mod eval; mod lower; -mod borrowck; -mod pretty; mod monomorphization; +mod pretty; pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason}; pub use eval::{ diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 671fd9ec3a4..9804910c878 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -1,14 +1,14 @@ -mod never_type; mod coercion; +mod diagnostics; +mod display_source_code; +mod incremental; +mod macros; +mod method_resolution; +mod never_type; +mod patterns; mod regression; mod simple; -mod patterns; mod traits; -mod method_resolution; -mod macros; -mod display_source_code; -mod incremental; -mod diagnostics; use std::{collections::HashMap, env}; diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 7b9f895bc73..fc0a196df7c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -2,7 +2,6 @@ use std::ops::ControlFlow; -use base_db::FileId; use hir_def::{ attr::AttrsWithOwner, item_scope::ItemInNs, @@ -11,12 +10,8 @@ use hir_def::{ resolver::{HasResolver, Resolver, TypeNs}, AssocItemId, AttrDefId, ModuleDefId, }; -use hir_expand::{ - name::Name, - span_map::{RealSpanMap, SpanMapRef}, -}; +use hir_expand::{mod_path::PathKind, name::Name}; use hir_ty::{db::HirDatabase, method_resolution}; -use syntax::{ast, AstNode}; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, @@ -129,7 +124,7 @@ fn resolve_doc_path_on_( AttrDefId::GenericParamId(_) => return None, }; - let mut modpath = modpath_from_str(db, link)?; + let mut modpath = modpath_from_str(link)?; let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); if resolved.is_none() { @@ -305,34 +300,37 @@ fn as_module_def_if_namespace_matches( (ns.unwrap_or(expected_ns) == expected_ns).then(|| DocLinkDef::ModuleDef(def)) } -fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option<ModPath> { +fn modpath_from_str(link: &str) -> Option<ModPath> { // FIXME: this is not how we should get a mod path here. let try_get_modpath = |link: &str| { - let ast_path = ast::SourceFile::parse(&format!("type T = {link};")) - .syntax_node() - .descendants() - .find_map(ast::Path::cast)?; - if ast_path.syntax().text() != link { - return None; - } - ModPath::from_src( - db.upcast(), - ast_path, - SpanMapRef::RealSpanMap(&RealSpanMap::absolute(FileId::BOGUS)), - ) + let mut parts = link.split("::"); + let mut first_segment = None; + let kind = match parts.next()? { + "" => PathKind::Abs, + "crate" => PathKind::Crate, + "self" => PathKind::Super(0), + "super" => { + let mut deg = 1; + while let Some(segment) = parts.next() { + if segment == "super" { + deg += 1; + } else { + first_segment = Some(segment); + break; + } + } + PathKind::Super(deg) + } + segment => { + first_segment = Some(segment); + PathKind::Plain + } + }; + let parts = first_segment.into_iter().chain(parts).map(|segment| match segment.parse() { + Ok(idx) => Name::new_tuple_field(idx), + Err(_) => Name::new_text_dont_use(segment.into()), + }); + Some(ModPath::from_segments(kind, parts)) }; - - let full = try_get_modpath(link); - if full.is_some() { - return full; - } - - // Tuple field names cannot be a part of `ModPath` usually, but rustdoc can - // resolve doc paths like `TupleStruct::0`. - // FIXME: Find a better way to handle these. - let (base, maybe_tuple_field) = link.rsplit_once("::")?; - let tuple_field = Name::new_tuple_field(maybe_tuple_field.parse().ok()?); - let mut modpath = try_get_modpath(base)?; - modpath.push_segment(tuple_field); - Some(modpath) + try_get_modpath(link) } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index c50be5f1141..c332ab0050c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -24,12 +24,12 @@ mod semantics; mod source_analyzer; -mod from_id; mod attrs; +mod from_id; mod has_source; -pub mod diagnostics; pub mod db; +pub mod diagnostics; pub mod symbols; mod display; @@ -70,13 +70,12 @@ use hir_ty::{ primitive::UintTy, traits::FnTrait, AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg, - GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, - TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId, - WhereClause, + GenericArgData, InferenceDiagnostic, Interner, ParamKind, QuantifiedWhereClause, Scalar, + Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, + ValueTyDefId, WhereClause, }; use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; -use once_cell::unsync::Lazy; use rustc_hash::FxHashSet; use stdx::{impl_from, never}; use syntax::{ @@ -1592,53 +1591,46 @@ impl DefWithBody { } for diag in source_map.diagnostics() { - match diag { - BodyDiagnostic::InactiveCode { node, cfg, opts } => acc.push( - InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into(), - ), - BodyDiagnostic::MacroError { node, message } => acc.push( - MacroError { - node: (*node).map(|it| it.into()), - precise_location: None, - message: message.to_string(), - } - .into(), - ), - BodyDiagnostic::UnresolvedProcMacro { node, krate } => acc.push( - UnresolvedProcMacro { - node: (*node).map(|it| it.into()), - precise_location: None, - macro_name: None, - kind: MacroKind::ProcMacro, - krate: *krate, - } - .into(), - ), - BodyDiagnostic::UnresolvedMacroCall { node, path } => acc.push( - UnresolvedMacroCall { - macro_call: (*node).map(|ast_ptr| ast_ptr.into()), - precise_location: None, - path: path.clone(), - is_bang: true, - } - .into(), - ), + acc.push(match diag { + BodyDiagnostic::InactiveCode { node, cfg, opts } => { + InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into() + } + BodyDiagnostic::MacroError { node, message } => MacroError { + node: (*node).map(|it| it.into()), + precise_location: None, + message: message.to_string(), + } + .into(), + BodyDiagnostic::UnresolvedProcMacro { node, krate } => UnresolvedProcMacro { + node: (*node).map(|it| it.into()), + precise_location: None, + macro_name: None, + kind: MacroKind::ProcMacro, + krate: *krate, + } + .into(), + BodyDiagnostic::UnresolvedMacroCall { node, path } => UnresolvedMacroCall { + macro_call: (*node).map(|ast_ptr| ast_ptr.into()), + precise_location: None, + path: path.clone(), + is_bang: true, + } + .into(), BodyDiagnostic::UnreachableLabel { node, name } => { - acc.push(UnreachableLabel { node: *node, name: name.clone() }.into()) + UnreachableLabel { node: *node, name: name.clone() }.into() } BodyDiagnostic::UndeclaredLabel { node, name } => { - acc.push(UndeclaredLabel { node: *node, name: name.clone() }.into()) + UndeclaredLabel { node: *node, name: name.clone() }.into() } - } + }); } let infer = db.infer(self.into()); - let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1); let expr_syntax = |expr| source_map.expr_syntax(expr).expect("unexpected synthetic"); let pat_syntax = |pat| source_map.pat_syntax(pat).expect("unexpected synthetic"); for d in &infer.diagnostics { - match d { - &hir_ty::InferenceDiagnostic::NoSuchField { field: expr, private } => { + acc.push(match d { + &InferenceDiagnostic::NoSuchField { field: expr, private } => { let expr_or_pat = match expr { ExprOrPatId::ExprId(expr) => { source_map.field_syntax(expr).map(AstPtr::wrap_left) @@ -1647,57 +1639,48 @@ impl DefWithBody { source_map.pat_field_syntax(pat).map(AstPtr::wrap_right) } }; - acc.push(NoSuchField { field: expr_or_pat, private }.into()) + NoSuchField { field: expr_or_pat, private }.into() } - &hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { - acc.push( - MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found } - .into(), - ) + &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { + MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found }.into() } - &hir_ty::InferenceDiagnostic::PrivateField { expr, field } => { + &InferenceDiagnostic::PrivateField { expr, field } => { let expr = expr_syntax(expr); let field = field.into(); - acc.push(PrivateField { expr, field }.into()) + PrivateField { expr, field }.into() } - &hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => { + &InferenceDiagnostic::PrivateAssocItem { id, item } => { let expr_or_pat = match id { ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left), ExprOrPatId::PatId(pat) => pat_syntax(pat).map(AstPtr::wrap_right), }; let item = item.into(); - acc.push(PrivateAssocItem { expr_or_pat, item }.into()) + PrivateAssocItem { expr_or_pat, item }.into() } - hir_ty::InferenceDiagnostic::ExpectedFunction { call_expr, found } => { + InferenceDiagnostic::ExpectedFunction { call_expr, found } => { let call_expr = expr_syntax(*call_expr); - - acc.push( - ExpectedFunction { - call: call_expr, - found: Type::new(db, DefWithBodyId::from(self), found.clone()), - } - .into(), - ) + ExpectedFunction { + call: call_expr, + found: Type::new(db, DefWithBodyId::from(self), found.clone()), + } + .into() } - hir_ty::InferenceDiagnostic::UnresolvedField { + InferenceDiagnostic::UnresolvedField { expr, receiver, name, method_with_same_name_exists, } => { let expr = expr_syntax(*expr); - - acc.push( - UnresolvedField { - expr, - name: name.clone(), - receiver: Type::new(db, DefWithBodyId::from(self), receiver.clone()), - method_with_same_name_exists: *method_with_same_name_exists, - } - .into(), - ) + UnresolvedField { + expr, + name: name.clone(), + receiver: Type::new(db, DefWithBodyId::from(self), receiver.clone()), + method_with_same_name_exists: *method_with_same_name_exists, + } + .into() } - hir_ty::InferenceDiagnostic::UnresolvedMethodCall { + InferenceDiagnostic::UnresolvedMethodCall { expr, receiver, name, @@ -1705,50 +1688,38 @@ impl DefWithBody { assoc_func_with_same_name, } => { let expr = expr_syntax(*expr); - - acc.push( - UnresolvedMethodCall { - expr, - name: name.clone(), - receiver: Type::new(db, DefWithBodyId::from(self), receiver.clone()), - field_with_same_name: field_with_same_name - .clone() - .map(|ty| Type::new(db, DefWithBodyId::from(self), ty)), - assoc_func_with_same_name: *assoc_func_with_same_name, - } - .into(), - ) + UnresolvedMethodCall { + expr, + name: name.clone(), + receiver: Type::new(db, DefWithBodyId::from(self), receiver.clone()), + field_with_same_name: field_with_same_name + .clone() + .map(|ty| Type::new(db, DefWithBodyId::from(self), ty)), + assoc_func_with_same_name: *assoc_func_with_same_name, + } + .into() } - &hir_ty::InferenceDiagnostic::UnresolvedAssocItem { id } => { + &InferenceDiagnostic::UnresolvedAssocItem { id } => { let expr_or_pat = match id { ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left), ExprOrPatId::PatId(pat) => pat_syntax(pat).map(AstPtr::wrap_right), }; - acc.push(UnresolvedAssocItem { expr_or_pat }.into()) + UnresolvedAssocItem { expr_or_pat }.into() } - &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { - expr, - is_break, - bad_value_break, - } => { + &InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => { let expr = expr_syntax(expr); - acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()) + BreakOutsideOfLoop { expr, is_break, bad_value_break }.into() } - hir_ty::InferenceDiagnostic::TypedHole { expr, expected } => { + InferenceDiagnostic::TypedHole { expr, expected } => { let expr = expr_syntax(*expr); - acc.push( - TypedHole { - expr, - expected: Type::new(db, DefWithBodyId::from(self), expected.clone()), - } - .into(), - ) + + TypedHole { + expr, + expected: Type::new(db, DefWithBodyId::from(self), expected.clone()), + } + .into() } - &hir_ty::InferenceDiagnostic::MismatchedTupleStructPatArgCount { - pat, - expected, - found, - } => { + &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => { let expr_or_pat = match pat { ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left), ExprOrPatId::PatId(pat) => { @@ -1762,11 +1733,9 @@ impl DefWithBody { InFile { file_id, value: ptr } } }; - acc.push( - MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into(), - ) + MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into() } - } + }); } for (pat_or_expr, mismatch) in infer.type_mismatches() { let expr_or_pat = match pat_or_expr { @@ -1805,8 +1774,6 @@ impl DefWithBody { } } - let hir_body = db.body(self.into()); - if let Ok(borrowck_results) = db.borrowck(self.into()) { for borrowck_result in borrowck_results.iter() { let mir_body = &borrowck_result.mir_body; @@ -1828,7 +1795,7 @@ impl DefWithBody { ) } let mol = &borrowck_result.mutability_of_locals; - for (binding_id, binding_data) in hir_body.bindings.iter() { + for (binding_id, binding_data) in body.bindings.iter() { if binding_data.problems.is_some() { // We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`. continue; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs index 3a0121f55fa..64e30b18345 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs @@ -116,11 +116,9 @@ trait AddRewrite { new: Vec<T>, target: TextRange, ) -> Option<()>; - fn yeet() {} } impl AddRewrite for Assists { - fn yeet() {} fn add_rewrite<T: AstNode>( &mut self, label: &str, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 1eb4903ab20..edcf52a9b38 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -111,6 +111,8 @@ mod handlers { mod add_label_to_loop; mod add_lifetime_to_type; mod add_missing_impl_members; + mod add_missing_match_arms; + mod add_return_type; mod add_turbo_fish; mod apply_demorgan; mod auto_import; @@ -124,15 +126,15 @@ mod handlers { mod convert_iter_for_each_to_for; mod convert_let_else_to_match; mod convert_match_to_let_else; + mod convert_named_struct_to_tuple_struct; mod convert_nested_function_to_closure; + mod convert_to_guarded_return; mod convert_tuple_return_type_to_struct; mod convert_tuple_struct_to_named_struct; - mod convert_named_struct_to_tuple_struct; - mod convert_to_guarded_return; mod convert_two_arm_bool_match_to_matches_macro; mod convert_while_to_loop; - mod desugar_doc_comment; mod destructure_tuple_binding; + mod desugar_doc_comment; mod expand_glob_import; mod extract_expressions_from_format_string; mod extract_function; @@ -140,7 +142,6 @@ mod handlers { mod extract_struct_from_enum_variant; mod extract_type_alias; mod extract_variable; - mod add_missing_match_arms; mod fix_visibility; mod flip_binexpr; mod flip_comma; @@ -148,6 +149,7 @@ mod handlers { mod generate_constant; mod generate_default_from_enum_variant; mod generate_default_from_new; + mod generate_delegate_methods; mod generate_delegate_trait; mod generate_deref; mod generate_derive; @@ -162,62 +164,60 @@ mod handlers { mod generate_is_empty_from_len; mod generate_mut_trait_impl; mod generate_new; - mod generate_delegate_methods; mod generate_trait_from_impl; - mod add_return_type; mod inline_call; mod inline_const_as_literal; mod inline_local_variable; mod inline_macro; mod inline_type_alias; + mod into_to_qualified_from; + mod introduce_named_generic; mod introduce_named_lifetime; mod invert_if; mod merge_imports; mod merge_match_arms; + mod merge_nested_if; mod move_bounds; mod move_const_to_impl; + mod move_from_mod_rs; mod move_guard; mod move_module_to_file; mod move_to_mod_rs; - mod move_from_mod_rs; mod number_representation; mod promote_local_to_const; mod pull_assignment_up; - mod qualify_path; mod qualify_method_call; + mod qualify_path; mod raw_string; mod remove_dbg; mod remove_mut; + mod remove_parentheses; mod remove_unused_imports; mod remove_unused_param; - mod remove_parentheses; mod reorder_fields; mod reorder_impl_items; - mod replace_try_expr_with_match; + mod replace_arith_op; mod replace_derive_with_manual_impl; mod replace_if_let_with_match; mod replace_is_method_with_if_let_method; - mod replace_method_eager_lazy; - mod replace_arith_op; - mod introduce_named_generic; mod replace_let_with_if_let; + mod replace_method_eager_lazy; mod replace_named_generic_with_impl; mod replace_qualified_name_with_use; mod replace_string_with_char; + mod replace_try_expr_with_match; mod replace_turbofish_with_explicit_type; - mod split_import; - mod unmerge_match_arm; - mod unwrap_tuple; mod sort_items; + mod split_import; mod toggle_ignore; + mod unmerge_match_arm; mod unmerge_use; mod unnecessary_async; + mod unqualify_method_call; mod unwrap_block; mod unwrap_result_return_type; - mod unqualify_method_call; + mod unwrap_tuple; mod wrap_return_type_in_result; - mod into_to_qualified_from; - mod merge_nested_if; pub(crate) fn all() -> &'static [Handler] { &[ diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 2420945f756..eeb3d80d07b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -23,8 +23,8 @@ use syntax::{ use crate::assist_context::{AssistContext, SourceChangeBuilder}; -pub(crate) mod suggest_name; mod gen_trait_fn_body; +pub(crate) mod suggest_name; pub(crate) fn unwrap_trivial_block(block_expr: ast::BlockExpr) -> ast::Expr { extract_trivial_expression(&block_expr) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 4d3d0b4d1a6..ba3c0cf3fd6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -2,8 +2,10 @@ pub(crate) mod attribute; pub(crate) mod dot; +pub(crate) mod env_vars; pub(crate) mod expr; pub(crate) mod extern_abi; +pub(crate) mod extern_crate; pub(crate) mod field; pub(crate) mod flyimport; pub(crate) mod fn_param; @@ -19,8 +21,6 @@ pub(crate) mod snippet; pub(crate) mod r#type; pub(crate) mod use_; pub(crate) mod vis; -pub(crate) mod env_vars; -pub(crate) mod extern_crate; use std::iter; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index 8f7c3b5070b..a7a6cdebd36 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -25,8 +25,8 @@ use crate::{ mod cfg; mod derive; mod lint; -mod repr; mod macro_use; +mod repr; pub(crate) use self::derive::complete_derive_path; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 6a98e109f6d..d26b6f431b5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -8,9 +8,9 @@ mod context; mod item; mod render; +mod snippet; #[cfg(test)] mod tests; -mod snippet; use ide_db::{ base_db::FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 6fd988bfc0f..ad26280ae74 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -1,14 +1,14 @@ //! `render` module provides utilities for rendering completion suggestions //! into code pieces that will be presented to user. -pub(crate) mod macro_; -pub(crate) mod function; pub(crate) mod const_; +pub(crate) mod function; +pub(crate) mod literal; +pub(crate) mod macro_; pub(crate) mod pattern; pub(crate) mod type_alias; -pub(crate) mod variant; pub(crate) mod union_literal; -pub(crate) mod literal; +pub(crate) mod variant; use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type}; use ide_db::{ diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index f13754e2ded..c421be51a0d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -12,8 +12,8 @@ mod attribute; mod expression; mod flyimport; mod fn_param; -mod item_list; mod item; +mod item_list; mod pattern; mod predicate; mod proc_macros; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 1cc1e363299..2881748dd47 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -9,6 +9,7 @@ mod apply_change; pub mod active_parameter; pub mod assists; pub mod defs; +pub mod documentation; pub mod famous_defs; pub mod helpers; pub mod items_locator; @@ -22,7 +23,6 @@ pub mod symbol_index; pub mod traits; pub mod ty_filter; pub mod use_trivial_constructor; -pub mod documentation; pub mod imports { pub mod import_assets; @@ -35,10 +35,10 @@ pub mod generated { } pub mod syntax_helpers { - pub mod node_ext; - pub mod insert_whitespace_into_node; pub mod format_string; pub mod format_string_exprs; + pub mod insert_whitespace_into_node; + pub mod node_ext; pub use parser::LexedStr; } @@ -414,6 +414,6 @@ impl SnippetCap { #[cfg(test)] mod tests { - mod sourcegen_lints; mod line_index; + mod sourcegen_lints; } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 5e950ecb0d1..e75d8973728 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -3,10 +3,10 @@ use hir::InFile; use ide_db::base_db::FileRange; use syntax::{ ast::{self, HasArgList}, - AstNode, SyntaxNodePtr, + AstNode, AstPtr, }; -use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext}; +use crate::{adjusted_display_range_new, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: mismatched-tuple-struct-pat-arg-count // @@ -24,7 +24,7 @@ pub(crate) fn mismatched_tuple_struct_pat_arg_count( Diagnostic::new( DiagnosticCode::RustcHardError("E0023"), message, - invalid_args_range(ctx, d.expr_or_pat.map(Into::into), d.expected, d.found), + invalid_args_range(ctx, d.expr_or_pat, d.expected, d.found), ) } @@ -40,17 +40,17 @@ pub(crate) fn mismatched_arg_count( Diagnostic::new( DiagnosticCode::RustcHardError("E0107"), message, - invalid_args_range(ctx, d.call_expr.map(Into::into), d.expected, d.found), + invalid_args_range(ctx, d.call_expr.map(AstPtr::wrap_left), d.expected, d.found), ) } fn invalid_args_range( ctx: &DiagnosticsContext<'_>, - source: InFile<SyntaxNodePtr>, + source: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>, expected: usize, found: usize, ) -> FileRange { - adjusted_display_range::<Either<ast::Expr, ast::TupleStructPat>>(ctx, source, &|expr| { + adjusted_display_range_new(ctx, source, &|expr| { let (text_range, r_paren_token, expected_arg) = match expr { Either::Left(ast::Expr::CallExpr(call)) => { let arg_list = call.arg_list()?; @@ -68,7 +68,7 @@ fn invalid_args_range( arg_list.args().nth(expected).map(|it| it.syntax().text_range()), ) } - Either::Right(pat) => { + Either::Right(ast::Pat::TupleStructPat(pat)) => { let r_paren = pat.r_paren_token()?; let l_paren = pat.l_paren_token()?; ( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index 5a20246cdd1..8d77e566edc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -129,6 +129,36 @@ mod tests { use crate::tests::{check_diagnostics, check_fix, check_no_fix}; #[test] + fn dont_work_for_field_with_disabled_cfg() { + check_diagnostics( + r#" +struct Test { + #[cfg(feature = "hello")] + test: u32, + other: u32 +} + +fn main() { + let a = Test { + #[cfg(feature = "hello")] + test: 1, + other: 1 + }; + + let Test { + #[cfg(feature = "hello")] + test, + mut other, + .. + } = a; + + other += 1; +} +"#, + ); + } + + #[test] fn no_such_field_diagnostics() { check_diagnostics( r#" diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 7743b060c86..f35fc5b533a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -44,30 +44,30 @@ mod handlers { pub(crate) mod private_assoc_item; pub(crate) mod private_field; pub(crate) mod replace_filter_map_next_with_find_map; - pub(crate) mod trait_impl_orphan; pub(crate) mod trait_impl_incorrect_safety; pub(crate) mod trait_impl_missing_assoc_item; + pub(crate) mod trait_impl_orphan; pub(crate) mod trait_impl_redundant_assoc_item; - pub(crate) mod typed_hole; pub(crate) mod type_mismatch; + pub(crate) mod typed_hole; + pub(crate) mod undeclared_label; pub(crate) mod unimplemented_builtin_macro; + pub(crate) mod unreachable_label; pub(crate) mod unresolved_assoc_item; pub(crate) mod unresolved_extern_crate; pub(crate) mod unresolved_field; - pub(crate) mod unresolved_method; pub(crate) mod unresolved_import; pub(crate) mod unresolved_macro_call; + pub(crate) mod unresolved_method; pub(crate) mod unresolved_module; pub(crate) mod unresolved_proc_macro; - pub(crate) mod undeclared_label; - pub(crate) mod unreachable_label; pub(crate) mod unused_variables; // The handlers below are unusual, the implement the diagnostics as well. pub(crate) mod field_shorthand; - pub(crate) mod useless_braces; - pub(crate) mod unlinked_file; pub(crate) mod json_is_not_rust; + pub(crate) mod unlinked_file; + pub(crate) mod useless_braces; } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs index d756e7a63eb..b5bf510aeed 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs @@ -69,11 +69,11 @@ // // foo($a, $b) ==>> ($a).foo($b) // ``` +mod fragments; mod from_comment; mod matching; mod nester; mod parsing; -mod fragments; mod replacing; mod resolving; mod search; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 79fff15f050..46e5901852a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -25,13 +25,13 @@ mod bind_pat; mod binding_mode; mod chaining; mod closing_brace; -mod closure_ret; mod closure_captures; +mod closure_ret; mod discriminant; mod fn_lifetime_fn; +mod implicit_drop; mod implicit_static; mod param_name; -mod implicit_drop; mod range_exclusive; #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 81682e07e09..e9f42d47855 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -21,24 +21,25 @@ macro_rules! eprintln { mod fixture; mod markup; -mod prime_caches; mod navigation_target; +mod prime_caches; mod annotations; mod call_hierarchy; -mod signature_help; mod doc_links; -mod highlight_related; mod expand_macro; mod extend_selection; +mod fetch_crates; mod file_structure; mod folding_ranges; mod goto_declaration; mod goto_definition; mod goto_implementation; mod goto_type_definition; +mod highlight_related; mod hover; mod inlay_hints; +mod interpret_function; mod join_lines; mod markdown_remove; mod matching_brace; @@ -48,6 +49,8 @@ mod parent_module; mod references; mod rename; mod runnables; +mod shuffle_crate_graph; +mod signature_help; mod ssr; mod static_index; mod status; @@ -56,12 +59,9 @@ mod syntax_tree; mod typing; mod view_crate_graph; mod view_hir; -mod view_mir; -mod interpret_function; mod view_item_tree; -mod shuffle_crate_graph; -mod fetch_crates; mod view_memory_layout; +mod view_mir; use std::ffi::OsStr; @@ -79,7 +79,7 @@ use syntax::SourceFile; use triomphe::Arc; use view_memory_layout::{view_memory_layout, RecursiveMemoryLayout}; -use crate::navigation_target::{ToNav, TryToNav}; +use crate::navigation_target::ToNav; pub use crate::{ annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation}, @@ -104,7 +104,7 @@ pub use crate::{ SymbolInformationKind, }, move_item::Direction, - navigation_target::{NavigationTarget, UpmappingResult}, + navigation_target::{NavigationTarget, TryToNav, UpmappingResult}, prime_caches::ParallelPrimeCachesProgress, references::ReferenceSearchResult, rename::RenameError, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index c8d7b7e25bb..bfd91feeb39 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -76,7 +76,7 @@ pub(crate) trait ToNav { fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget>; } -pub(crate) trait TryToNav { +pub trait TryToNav { fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>>; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 3607c486d7d..8c6f5e2e9cb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -3,11 +3,11 @@ pub(crate) mod tags; mod highlights; mod injector; -mod highlight; +mod escape; mod format; -mod macro_; +mod highlight; mod inject; -mod escape; +mod macro_; mod html; #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index f968a89a441..62fdce36892 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -8,11 +8,11 @@ #![warn(rust_2018_idioms, unused_lifetimes)] -mod parser; mod expander; +mod parser; mod syntax_bridge; -mod tt_iter; mod to_parser_input; +mod tt_iter; #[cfg(test)] mod benchmark; diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs index 8b57d7eeaf5..4513732902d 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -234,7 +234,7 @@ where let mut stack = NonEmptyVec::new(entry); while let Some((token, abs_range)) = conv.bump() { - let tt::Subtree { delimiter, token_trees: result } = stack.last_mut(); + let tt::Subtree { delimiter, token_trees } = stack.last_mut(); let tt = match token.as_leaf() { Some(leaf) => tt::TokenTree::Leaf(leaf.clone()), @@ -243,7 +243,7 @@ where COMMENT => { let span = conv.span_for(abs_range); if let Some(tokens) = conv.convert_doc_comment(&token, span) { - result.extend(tokens); + token_trees.extend(tokens); } continue; } @@ -317,7 +317,7 @@ where span: conv .span_for(TextRange::at(abs_range.start(), TextSize::of('\''))), }); - result.push(apostrophe.into()); + token_trees.push(apostrophe.into()); let ident = tt::Leaf::from(tt::Ident { text: SmolStr::new(&token.to_text(conv)[1..]), @@ -326,7 +326,7 @@ where abs_range.end(), )), }); - result.push(ident.into()); + token_trees.push(ident.into()); continue; } _ => continue, @@ -337,7 +337,7 @@ where }, }; - result.push(tt); + token_trees.push(tt); } // If we get here, we've consumed all input tokens. diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index 19da297b58c..53fda3ae4fd 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -30,12 +30,12 @@ mod attributes; mod expressions; +mod generic_args; +mod generic_params; mod items; mod params; mod paths; mod patterns; -mod generic_args; -mod generic_params; mod types; use crate::{ diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs index caf2a005a7d..243a219525a 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs @@ -1,5 +1,5 @@ -mod consts; mod adt; +mod consts; mod traits; mod use_item; diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs index ed0aec3cab3..3ca285e787e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lib.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs @@ -26,15 +26,15 @@ extern crate ra_ap_rustc_lexer as rustc_lexer; #[cfg(feature = "in-rust-tree")] extern crate rustc_lexer; -mod lexed_str; -mod token_set; -mod syntax_kind; mod event; -mod parser; mod grammar; mod input; +mod lexed_str; mod output; +mod parser; mod shortcuts; +mod syntax_kind; +mod token_set; #[cfg(test)] mod tests; diff --git a/src/tools/rust-analyzer/crates/parser/src/tests.rs b/src/tools/rust-analyzer/crates/parser/src/tests.rs index 2fec765bd78..c65219b28dc 100644 --- a/src/tools/rust-analyzer/crates/parser/src/tests.rs +++ b/src/tools/rust-analyzer/crates/parser/src/tests.rs @@ -1,6 +1,6 @@ +mod prefix_entries; mod sourcegen_inline_tests; mod top_entries; -mod prefix_entries; use std::{ fmt::Write, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs index e6903fb8d4a..c9b605a8087 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs @@ -100,7 +100,8 @@ fn main() { .current_dir(&staging_dir) .args(["pkgid", name]) .output() - .unwrap().stdout, + .unwrap() + .stdout, ) .unwrap(); let pkgid = pkgid.trim(); @@ -109,7 +110,8 @@ fn main() { for message in Message::parse_stream(output.stdout.as_slice()) { if let Message::CompilerArtifact(artifact) = message.unwrap() { if artifact.target.kind.contains(&"proc-macro".to_string()) { - if artifact.package_id.repr.starts_with(&repr) || artifact.package_id.repr == pkgid { + if artifact.package_id.repr.starts_with(&repr) || artifact.package_id.repr == pkgid + { artifact_path = Some(PathBuf::from(&artifact.filenames[0])); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 67b9f57a163..460a96c07f3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -21,8 +21,8 @@ extern crate proc_macro; extern crate rustc_driver as _; mod dylib; -mod server; mod proc_macros; +mod server; use std::{ collections::{hash_map::Entry, HashMap}, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs index 1854322ddb5..ff8fd295d88 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs @@ -13,9 +13,9 @@ use proc_macro::bridge; mod token_stream; pub use token_stream::TokenStream; -pub mod token_id; pub mod rust_analyzer_span; mod symbol; +pub mod token_id; pub use symbol::*; use tt::Spacing; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_id.rs index 12526ad4f3a..c83e09af0d6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_id.rs @@ -206,7 +206,7 @@ impl server::TokenStream for TokenIdServer { stream: if subtree.token_trees.is_empty() { None } else { - Some(subtree.token_trees.into_iter().collect()) + Some(TokenStream { token_trees: subtree.token_trees }) }, span: bridge::DelimSpan::from_single(subtree.delimiter.open), }), diff --git a/src/tools/rust-analyzer/crates/profile/src/lib.rs b/src/tools/rust-analyzer/crates/profile/src/lib.rs index fdd724e2aab..d86aa0c4144 100644 --- a/src/tools/rust-analyzer/crates/profile/src/lib.rs +++ b/src/tools/rust-analyzer/crates/profile/src/lib.rs @@ -2,11 +2,11 @@ #![warn(rust_2018_idioms, unused_lifetimes)] -mod stop_watch; -mod memory_usage; #[cfg(feature = "cpu_profiler")] mod google_cpu_profiler; mod hprof; +mod memory_usage; +mod stop_watch; mod tree; use std::cell::RefCell; diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index 5f9b708289d..5114c9c016d 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -17,15 +17,15 @@ #![warn(rust_2018_idioms, unused_lifetimes)] -mod manifest_path; +mod build_scripts; mod cargo_workspace; mod cfg_flag; +mod manifest_path; mod project_json; -mod sysroot; -mod workspace; mod rustc_cfg; -mod build_scripts; +mod sysroot; pub mod target_data_layout; +mod workspace; #[cfg(test)] mod tests; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs index de00c4192b4..00670f2cb4c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs @@ -1,16 +1,16 @@ //! Various batch processing tasks, intended primarily for debugging. -pub mod flags; -mod parse; -mod symbols; -mod highlight; mod analysis_stats; mod diagnostics; -mod ssr; +pub mod flags; +mod highlight; mod lsif; -mod scip; +mod parse; mod run_tests; mod rustc_tests; +mod scip; +mod ssr; +mod symbols; mod progress_report; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index f3c2df24d95..1a55dcebc13 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1319,6 +1319,9 @@ pub(crate) fn handle_code_lens_resolve( snap: GlobalStateSnapshot, code_lens: CodeLens, ) -> anyhow::Result<CodeLens> { + if code_lens.data.is_none() { + return Ok(code_lens); + } let Some(annotation) = from_proto::annotation(&snap, code_lens.clone())? else { return Ok(code_lens); }; @@ -1327,13 +1330,14 @@ pub(crate) fn handle_code_lens_resolve( let mut acc = Vec::new(); to_proto::code_lens(&mut acc, &snap, annotation)?; - let res = match acc.pop() { + let mut res = match acc.pop() { Some(it) if acc.is_empty() => it, _ => { never!(); code_lens } }; + res.data = None; Ok(res) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs index ac7e1a95e62..9e0d42faed4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs @@ -2,11 +2,11 @@ use core::fmt; -pub(crate) mod utils; -pub(crate) mod semantic_tokens; pub mod ext; pub(crate) mod from_proto; +pub(crate) mod semantic_tokens; pub(crate) mod to_proto; +pub(crate) mod utils; #[derive(Debug)] pub(crate) struct LspError { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs index db192cf8fe5..d3146ab7671 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -154,6 +154,7 @@ fn check_licenses() { Apache-2.0 Apache-2.0 OR BSL-1.0 Apache-2.0 OR MIT +Apache-2.0 WITH LLVM-exception Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT Apache-2.0/MIT BSD-3-Clause diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index f6569050b4a..6796dc41886 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -1,7 +1,4 @@ //! File and span related types. -// FIXME: This should be moved into its own crate to get rid of the dependency inversion, base-db -// has business depending on tt, tt should depend on a span crate only (which unforunately will have -// to depend on salsa) use std::fmt::{self, Write}; use salsa::InternId; diff --git a/src/tools/rust-analyzer/crates/stdx/src/lib.rs b/src/tools/rust-analyzer/crates/stdx/src/lib.rs index cd5285295a2..07b78272281 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/lib.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/lib.rs @@ -6,13 +6,13 @@ use std::io as sio; use std::process::Command; use std::{cmp::Ordering, ops, time::Instant}; +pub mod anymap; mod macros; -pub mod process; -pub mod panic_context; pub mod non_empty_vec; +pub mod panic_context; +pub mod process; pub mod rand; pub mod thread; -pub mod anymap; pub use always_assert::{always, never}; pub use itertools; diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index cc90d2dd1d5..e9ab7a4320b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -1,15 +1,15 @@ //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s -mod generated; -mod traits; -mod token_ext; -mod node_ext; -mod expr_ext; -mod operators; pub mod edit; pub mod edit_in_place; +mod expr_ext; +mod generated; pub mod make; +mod node_ext; +mod operators; pub mod prec; +mod token_ext; +mod traits; use std::marker::PhantomData; diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index 21ed1310f56..62a0261d7a4 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -32,22 +32,22 @@ macro_rules! eprintln { ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; } -mod syntax_node; -mod syntax_error; mod parsing; -mod validation; mod ptr; -mod token_text; +mod syntax_error; +mod syntax_node; #[cfg(test)] mod tests; +mod token_text; +mod validation; pub mod algo; pub mod ast; #[doc(hidden)] pub mod fuzz; -pub mod utils; -pub mod ted; pub mod hacks; +pub mod ted; +pub mod utils; use std::marker::PhantomData; diff --git a/src/tools/rust-analyzer/editors/code/language-configuration.json b/src/tools/rust-analyzer/editors/code/language-configuration.json index 1c348b63f1a..a2af8b51a90 100644 --- a/src/tools/rust-analyzer/editors/code/language-configuration.json +++ b/src/tools/rust-analyzer/editors/code/language-configuration.json @@ -6,7 +6,9 @@ "brackets": [ ["{", "}"], ["[", "]"], - ["(", ")"] + ["(", ")"], + ["#[", "]"], + ["#![", "]"] ], "colorizedBracketPairs": [ ["{", "}"], @@ -17,6 +19,8 @@ { "open": "{", "close": "}" }, { "open": "[", "close": "]" }, { "open": "(", "close": ")" }, + { "open": "#[", "close": "]" }, + { "open": "#![", "close": "]" }, { "open": "\"", "close": "\"", "notIn": ["string"] }, { "open": "/*", "close": " */" }, { "open": "`", "close": "`", "notIn": ["string"] } diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs index f717f8e0d4b..e476f8c2d13 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs @@ -6,11 +6,11 @@ #![warn(rust_2018_idioms, unused_lifetimes)] -mod msg; -mod stdio; mod error; -mod socket; +mod msg; mod req_queue; +mod socket; +mod stdio; use std::{ io, diff --git a/src/tools/rust-analyzer/rustfmt.toml b/src/tools/rust-analyzer/rustfmt.toml index 71007de81b9..20bf59547b8 100644 --- a/src/tools/rust-analyzer/rustfmt.toml +++ b/src/tools/rust-analyzer/rustfmt.toml @@ -1,2 +1,2 @@ -reorder_modules = false +reorder_modules = true use_small_heuristics = "Max" diff --git a/src/tools/rust-analyzer/xtask/src/main.rs b/src/tools/rust-analyzer/xtask/src/main.rs index 49f8ae79baf..2d40ceb737d 100644 --- a/src/tools/rust-analyzer/xtask/src/main.rs +++ b/src/tools/rust-analyzer/xtask/src/main.rs @@ -12,11 +12,11 @@ mod flags; -mod install; -mod release; mod dist; -mod publish; +mod install; mod metrics; +mod publish; +mod release; use anyhow::bail; use std::{ diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index b06c072a2b0..d141de3c2c5 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -132,6 +132,7 @@ const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[ ("dissimilar", "Apache-2.0"), ("notify", "CC0-1.0"), ("pulldown-cmark-to-cmark", "Apache-2.0"), + ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 ("scip", "Apache-2.0"), ("snap", "BSD-3-Clause"), diff --git a/src/version b/src/version index 79e15fd4937..54227249d1f 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.77.0 +1.78.0 diff --git a/tests/codegen/pow_of_two.rs b/tests/codegen/pow_of_two.rs deleted file mode 100644 index 372360dfd12..00000000000 --- a/tests/codegen/pow_of_two.rs +++ /dev/null @@ -1,55 +0,0 @@ -// compile-flags: --crate-type=lib -Zmerge-functions=disabled -O -C overflow-checks=false - -// CHECK-LABEL: @a( -#[no_mangle] -pub fn a(exp: u32) -> u64 { - // CHECK: %{{[^ ]+}} = icmp ugt i32 %exp, 64 - // CHECK: %{{[^ ]+}} = zext{{( nneg)?}} i32 %exp to i64 - // CHECK: %{{[^ ]+}} = shl nuw i64 {{[^ ]+}}, %{{[^ ]+}} - // CHECK: ret i64 %{{[^ ]+}} - 2u64.pow(exp) -} - -// CHECK-LABEL: @b( -#[no_mangle] -pub fn b(exp: u32) -> i64 { - // CHECK: %{{[^ ]+}} = icmp ugt i32 %exp, 64 - // CHECK: %{{[^ ]+}} = zext{{( nneg)?}} i32 %exp to i64 - // CHECK: %{{[^ ]+}} = shl nuw i64 {{[^ ]+}}, %{{[^ ]+}} - // CHECK: ret i64 %{{[^ ]+}} - 2i64.pow(exp) -} - -// CHECK-LABEL: @c( -#[no_mangle] -pub fn c(exp: u32) -> u32 { - // CHECK: %{{[^ ]+}} = icmp ugt i32 %exp, 16 - // CHECK: %{{[^ ]+}} = shl nuw nsw i32 %exp, 1 - // CHECK: %{{[^ ]+}} = shl nuw i32 1, %{{[^ ]+}} - // CHECK: %{{[^ ]+}} = select i1 %{{[^ ]+}}, i32 0, i32 %{{[^ ]+}} - // CHECK: ret i32 %{{[^ ]+}} - 4u32.pow(exp) -} - -// CHECK-LABEL: @d( -#[no_mangle] -pub fn d(exp: u32) -> u32 { - // CHECK: %{{[^ ]+}} = icmp ugt i32 %exp, 6 - // CHECK: %{{[^ ]+}} = mul nuw nsw i32 %exp, 5 - // CHECK: %{{[^ ]+}} = shl nuw nsw i32 1, %{{[^ ]+}} - // CHECK: %{{[^ ]+}} = select i1 {{[^ ]+}}, i32 0, i32 %{{[^ ]+}} - // CHECK: ret i32 %{{[^ ]+}} - 32u32.pow(exp) -} - -// CHECK-LABEL: @e( -#[no_mangle] -pub fn e(exp: u32) -> i32 { - // CHECK: %{{[^ ]+}} = icmp ugt i32 %exp, 6 - // CHECK: %{{[^ ]+}} = mul nuw {{(nsw )?}}i32 %exp, 5 - // CHECK: %{{[^ ]+}} = shl nuw {{(nsw )?}}i32 1, %{{[^ ]+}} - // CHECK: %{{[^ ]+}} = select i1 {{[^ ]+}}, i32 0, i32 %{{[^ ]+}} - // CHECK: ret i32 %{{[^ ]+}} - 32i32.pow(exp) -} -// note: d and e are expected to yield the same IR diff --git a/tests/coverage/bad_counter_ids.cov-map b/tests/coverage/bad_counter_ids.cov-map index 3b51e3ef9de..7eff7f5f02f 100644 --- a/tests/coverage/bad_counter_ids.cov-map +++ b/tests/coverage/bad_counter_ids.cov-map @@ -1,81 +1,81 @@ Function name: bad_counter_ids::eq_bad -Raw bytes (14): 0x[01, 01, 00, 02, 01, 23, 01, 02, 1f, 00, 03, 01, 00, 02] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 24, 01, 02, 1f, 00, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 2 -- Code(Counter(0)) at (prev + 35, 1) to (start + 2, 31) +- Code(Counter(0)) at (prev + 36, 1) to (start + 2, 31) - Code(Zero) at (prev + 3, 1) to (start + 0, 2) Function name: bad_counter_ids::eq_bad_message -Raw bytes (21): 0x[01, 01, 01, 01, 00, 03, 01, 28, 01, 02, 0f, 02, 02, 20, 00, 2b, 00, 01, 01, 00, 02] +Raw bytes (21): 0x[01, 01, 01, 01, 00, 03, 01, 29, 01, 02, 0f, 02, 02, 20, 00, 2b, 00, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Zero Number of file 0 mappings: 3 -- Code(Counter(0)) at (prev + 40, 1) to (start + 2, 15) +- Code(Counter(0)) at (prev + 41, 1) to (start + 2, 15) - Code(Expression(0, Sub)) at (prev + 2, 32) to (start + 0, 43) = (c0 - Zero) - Code(Zero) at (prev + 1, 1) to (start + 0, 2) Function name: bad_counter_ids::eq_good -Raw bytes (14): 0x[01, 01, 00, 02, 01, 0f, 01, 02, 1f, 05, 03, 01, 00, 02] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 10, 01, 02, 1f, 05, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 2 -- Code(Counter(0)) at (prev + 15, 1) to (start + 2, 31) +- Code(Counter(0)) at (prev + 16, 1) to (start + 2, 31) - Code(Counter(1)) at (prev + 3, 1) to (start + 0, 2) Function name: bad_counter_ids::eq_good_message -Raw bytes (19): 0x[01, 01, 00, 03, 01, 14, 01, 02, 0f, 00, 02, 20, 00, 2b, 05, 01, 01, 00, 02] +Raw bytes (19): 0x[01, 01, 00, 03, 01, 15, 01, 02, 0f, 00, 02, 20, 00, 2b, 05, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 3 -- Code(Counter(0)) at (prev + 20, 1) to (start + 2, 15) +- Code(Counter(0)) at (prev + 21, 1) to (start + 2, 15) - Code(Zero) at (prev + 2, 32) to (start + 0, 43) - Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2) Function name: bad_counter_ids::ne_bad -Raw bytes (14): 0x[01, 01, 00, 02, 01, 2d, 01, 02, 1f, 00, 03, 01, 00, 02] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 2e, 01, 02, 1f, 00, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 2 -- Code(Counter(0)) at (prev + 45, 1) to (start + 2, 31) +- Code(Counter(0)) at (prev + 46, 1) to (start + 2, 31) - Code(Zero) at (prev + 3, 1) to (start + 0, 2) Function name: bad_counter_ids::ne_bad_message -Raw bytes (19): 0x[01, 01, 00, 03, 01, 32, 01, 02, 0f, 05, 02, 20, 00, 2b, 00, 01, 01, 00, 02] +Raw bytes (19): 0x[01, 01, 00, 03, 01, 33, 01, 02, 0f, 05, 02, 20, 00, 2b, 00, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 3 -- Code(Counter(0)) at (prev + 50, 1) to (start + 2, 15) +- Code(Counter(0)) at (prev + 51, 1) to (start + 2, 15) - Code(Counter(1)) at (prev + 2, 32) to (start + 0, 43) - Code(Zero) at (prev + 1, 1) to (start + 0, 2) Function name: bad_counter_ids::ne_good -Raw bytes (16): 0x[01, 01, 01, 01, 00, 02, 01, 19, 01, 02, 1f, 02, 03, 01, 00, 02] +Raw bytes (16): 0x[01, 01, 01, 01, 00, 02, 01, 1a, 01, 02, 1f, 02, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Zero Number of file 0 mappings: 2 -- Code(Counter(0)) at (prev + 25, 1) to (start + 2, 31) +- Code(Counter(0)) at (prev + 26, 1) to (start + 2, 31) - Code(Expression(0, Sub)) at (prev + 3, 1) to (start + 0, 2) = (c0 - Zero) Function name: bad_counter_ids::ne_good_message -Raw bytes (21): 0x[01, 01, 01, 01, 00, 03, 01, 1e, 01, 02, 0f, 00, 02, 20, 00, 2b, 02, 01, 01, 00, 02] +Raw bytes (21): 0x[01, 01, 01, 01, 00, 03, 01, 1f, 01, 02, 0f, 00, 02, 20, 00, 2b, 02, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Zero Number of file 0 mappings: 3 -- Code(Counter(0)) at (prev + 30, 1) to (start + 2, 15) +- Code(Counter(0)) at (prev + 31, 1) to (start + 2, 15) - Code(Zero) at (prev + 2, 32) to (start + 0, 43) - Code(Expression(0, Sub)) at (prev + 1, 1) to (start + 0, 2) = (c0 - Zero) diff --git a/tests/coverage/bad_counter_ids.coverage b/tests/coverage/bad_counter_ids.coverage index 5474ec2f785..39e6cae11dd 100644 --- a/tests/coverage/bad_counter_ids.coverage +++ b/tests/coverage/bad_counter_ids.coverage @@ -1,5 +1,6 @@ LL| |#![feature(coverage_attribute)] - LL| |// compile-flags: --edition=2021 -Copt-level=0 -Zmir-opt-level=3 + LL| |// edition: 2021 + LL| |// compile-flags: -Copt-level=0 -Zmir-opt-level=3 LL| | LL| |// Regression test for <https://github.com/rust-lang/rust/issues/117012>. LL| |// diff --git a/tests/coverage/bad_counter_ids.rs b/tests/coverage/bad_counter_ids.rs index ef5460102b7..e22b96468ab 100644 --- a/tests/coverage/bad_counter_ids.rs +++ b/tests/coverage/bad_counter_ids.rs @@ -1,5 +1,6 @@ #![feature(coverage_attribute)] -// compile-flags: --edition=2021 -Copt-level=0 -Zmir-opt-level=3 +// edition: 2021 +// compile-flags: -Copt-level=0 -Zmir-opt-level=3 // Regression test for <https://github.com/rust-lang/rust/issues/117012>. // diff --git a/tests/coverage/closure_macro.coverage b/tests/coverage/closure_macro.coverage index a6eeb79a71c..ab152a91356 100644 --- a/tests/coverage/closure_macro.coverage +++ b/tests/coverage/closure_macro.coverage @@ -1,5 +1,5 @@ - LL| |// compile-flags: --edition=2018 LL| |#![feature(coverage_attribute)] + LL| |// edition: 2018 LL| | LL| |macro_rules! bail { LL| | ($msg:literal $(,)?) => { diff --git a/tests/coverage/closure_macro.rs b/tests/coverage/closure_macro.rs index c3ef9167880..38a81ea434b 100644 --- a/tests/coverage/closure_macro.rs +++ b/tests/coverage/closure_macro.rs @@ -1,5 +1,5 @@ -// compile-flags: --edition=2018 #![feature(coverage_attribute)] +// edition: 2018 macro_rules! bail { ($msg:literal $(,)?) => { diff --git a/tests/coverage/fn_sig_into_try.coverage b/tests/coverage/fn_sig_into_try.coverage index 71e15fc11eb..aac5c9d9a94 100644 --- a/tests/coverage/fn_sig_into_try.coverage +++ b/tests/coverage/fn_sig_into_try.coverage @@ -1,5 +1,5 @@ LL| |#![feature(coverage_attribute)] - LL| |// compile-flags: --edition=2021 + LL| |// edition: 2021 LL| | LL| |// Regression test for inconsistent handling of function signature spans that LL| |// are followed by code using the `?` operator. diff --git a/tests/coverage/fn_sig_into_try.rs b/tests/coverage/fn_sig_into_try.rs index 1f4cc67af83..5d8e7929ef9 100644 --- a/tests/coverage/fn_sig_into_try.rs +++ b/tests/coverage/fn_sig_into_try.rs @@ -1,5 +1,5 @@ #![feature(coverage_attribute)] -// compile-flags: --edition=2021 +// edition: 2021 // Regression test for inconsistent handling of function signature spans that // are followed by code using the `?` operator. diff --git a/tests/coverage/issue-93054.cov-map b/tests/coverage/issue-93054.cov-map index c2c6e9a6516..024ef519fcf 100644 --- a/tests/coverage/issue-93054.cov-map +++ b/tests/coverage/issue-93054.cov-map @@ -1,24 +1,24 @@ Function name: issue_93054::foo2 (unused) -Raw bytes (9): 0x[01, 01, 00, 01, 00, 16, 01, 00, 1d] +Raw bytes (9): 0x[01, 01, 00, 01, 00, 15, 01, 00, 1d] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Zero) at (prev + 22, 1) to (start + 0, 29) +- Code(Zero) at (prev + 21, 1) to (start + 0, 29) Function name: issue_93054::main -Raw bytes (9): 0x[01, 01, 00, 01, 01, 1e, 01, 00, 0d] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 1d, 01, 00, 0d] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 30, 1) to (start + 0, 13) +- Code(Counter(0)) at (prev + 29, 1) to (start + 0, 13) Function name: issue_93054::make (unused) -Raw bytes (9): 0x[01, 01, 00, 01, 00, 1a, 01, 02, 02] +Raw bytes (9): 0x[01, 01, 00, 01, 00, 19, 01, 02, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Zero) at (prev + 26, 1) to (start + 2, 2) +- Code(Zero) at (prev + 25, 1) to (start + 2, 2) diff --git a/tests/coverage/issue-93054.coverage b/tests/coverage/issue-93054.coverage index 15f225326a4..6ae8ffb5cb4 100644 --- a/tests/coverage/issue-93054.coverage +++ b/tests/coverage/issue-93054.coverage @@ -1,11 +1,10 @@ LL| |#![allow(dead_code, unreachable_code)] + LL| |// edition: 2021 LL| | LL| |// Regression test for #93054: Functions using uninhabited types often only have a single, LL| |// unreachable basic block which doesn't get instrumented. This should not cause llvm-cov to fail. LL| |// Since these kinds functions can't be invoked anyway, it's ok to not have coverage data for them. LL| | - LL| |// compile-flags: --edition=2021 - LL| | LL| |enum Never {} LL| | LL| |impl Never { diff --git a/tests/coverage/issue-93054.rs b/tests/coverage/issue-93054.rs index da546cfeef8..f16fc1e4239 100644 --- a/tests/coverage/issue-93054.rs +++ b/tests/coverage/issue-93054.rs @@ -1,11 +1,10 @@ #![allow(dead_code, unreachable_code)] +// edition: 2021 // Regression test for #93054: Functions using uninhabited types often only have a single, // unreachable basic block which doesn't get instrumented. This should not cause llvm-cov to fail. // Since these kinds functions can't be invoked anyway, it's ok to not have coverage data for them. -// compile-flags: --edition=2021 - enum Never {} impl Never { diff --git a/tests/coverage/long_and_wide.coverage b/tests/coverage/long_and_wide.coverage index d7d29ca40cd..cf4dd281140 100644 --- a/tests/coverage/long_and_wide.coverage +++ b/tests/coverage/long_and_wide.coverage @@ -1,4 +1,4 @@ - LL| |// compile-flags: --edition=2021 + LL| |// edition: 2021 LL| |// ignore-tidy-linelength LL| | LL| |// This file deliberately contains line and column numbers larger than 127, diff --git a/tests/coverage/long_and_wide.rs b/tests/coverage/long_and_wide.rs index a7cbcd48027..87f2207bd7e 100644 --- a/tests/coverage/long_and_wide.rs +++ b/tests/coverage/long_and_wide.rs @@ -1,4 +1,4 @@ -// compile-flags: --edition=2021 +// edition: 2021 // ignore-tidy-linelength // This file deliberately contains line and column numbers larger than 127, diff --git a/tests/coverage/sort_groups.coverage b/tests/coverage/sort_groups.coverage index c70d7b3b282..77cbb09c74a 100644 --- a/tests/coverage/sort_groups.coverage +++ b/tests/coverage/sort_groups.coverage @@ -1,4 +1,4 @@ - LL| |// compile-flags: --edition=2021 + LL| |// edition: 2021 LL| | LL| |// Demonstrate that `sort_subviews.py` can sort instantiation groups into a LL| |// predictable order, while preserving their heterogeneous contents. diff --git a/tests/coverage/sort_groups.rs b/tests/coverage/sort_groups.rs index 5adbbc6a87d..17fd862ca2c 100644 --- a/tests/coverage/sort_groups.rs +++ b/tests/coverage/sort_groups.rs @@ -1,4 +1,4 @@ -// compile-flags: --edition=2021 +// edition: 2021 // Demonstrate that `sort_subviews.py` can sort instantiation groups into a // predictable order, while preserving their heterogeneous contents. diff --git a/tests/coverage/trivial.coverage b/tests/coverage/trivial.coverage index 4f417979ef9..af4be7b8f33 100644 --- a/tests/coverage/trivial.coverage +++ b/tests/coverage/trivial.coverage @@ -1,4 +1,4 @@ - LL| |// compile-flags: --edition=2021 + LL| |// edition: 2021 LL| | LL| 1|fn main() {} diff --git a/tests/coverage/trivial.rs b/tests/coverage/trivial.rs index d0a9b44fb36..782472739a0 100644 --- a/tests/coverage/trivial.rs +++ b/tests/coverage/trivial.rs @@ -1,3 +1,3 @@ -// compile-flags: --edition=2021 +// edition: 2021 fn main() {} diff --git a/tests/coverage/unreachable.coverage b/tests/coverage/unreachable.coverage index 7015bb90aa3..a58765f688b 100644 --- a/tests/coverage/unreachable.coverage +++ b/tests/coverage/unreachable.coverage @@ -1,6 +1,6 @@ LL| |#![feature(core_intrinsics)] LL| |#![feature(coverage_attribute)] - LL| |// compile-flags: --edition=2021 + LL| |// edition: 2021 LL| | LL| |// <https://github.com/rust-lang/rust/issues/116171> LL| |// If we instrument a function for coverage, but all of its counter-increment diff --git a/tests/coverage/unreachable.rs b/tests/coverage/unreachable.rs index 6385bfa160d..6d0c7b3ca8d 100644 --- a/tests/coverage/unreachable.rs +++ b/tests/coverage/unreachable.rs @@ -1,6 +1,6 @@ #![feature(core_intrinsics)] #![feature(coverage_attribute)] -// compile-flags: --edition=2021 +// edition: 2021 // <https://github.com/rust-lang/rust/issues/116171> // If we instrument a function for coverage, but all of its counter-increment diff --git a/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs b/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs index 785da11b9b2..3056ebb7575 100644 --- a/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs +++ b/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs @@ -19,6 +19,6 @@ use rustc_errors::{Applicability, MultiSpan}; extern crate rustc_session; #[derive(Diagnostic)] -#[diag(compiletest_example, code = 0123)] +#[diag(compiletest_example, code = E0123)] //~^ ERROR diagnostic slug and crate name do not match struct Hello {} diff --git a/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr b/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr index eda24a555f8..df1bad3cad0 100644 --- a/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr +++ b/tests/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr @@ -1,7 +1,7 @@ error: diagnostic slug and crate name do not match --> $DIR/enforce_slug_naming.rs:22:8 | -LL | #[diag(compiletest_example, code = 0123)] +LL | #[diag(compiletest_example, code = E0123)] | ^^^^^^^^^^^^^^^^^^^ | = note: slug is `compiletest_example` but the crate name is `rustc_dummy` diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 814d4736197..652e573d6ef 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -143,7 +143,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_feature = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `f`, `f16c`, `f32mm`, `f64mm`, `fast-unaligned-access`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, `zkt` + = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `f`, `f16c`, `f32mm`, `f64mm`, `fast-unaligned-access`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, `zkt` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` diff --git a/tests/ui/resolve/change-ty-to-const-param-sugg-0.rs b/tests/ui/resolve/change-ty-to-const-param-sugg-0.rs new file mode 100644 index 00000000000..36fe16f64eb --- /dev/null +++ b/tests/ui/resolve/change-ty-to-const-param-sugg-0.rs @@ -0,0 +1,10 @@ +fn make<N: u32>() {} +//~^ ERROR expected trait, found builtin type `u32` +//~| HELP you might have meant to write a const parameter here + +struct Array<N: usize>([bool; N]); +//~^ ERROR expected trait, found builtin type `usize` +//~| HELP you might have meant to write a const parameter here +//~| ERROR expected value, found type parameter `N` + +fn main() {} diff --git a/tests/ui/resolve/change-ty-to-const-param-sugg-0.stderr b/tests/ui/resolve/change-ty-to-const-param-sugg-0.stderr new file mode 100644 index 00000000000..4e411eae3cb --- /dev/null +++ b/tests/ui/resolve/change-ty-to-const-param-sugg-0.stderr @@ -0,0 +1,34 @@ +error[E0404]: expected trait, found builtin type `u32` + --> $DIR/change-ty-to-const-param-sugg-0.rs:1:12 + | +LL | fn make<N: u32>() {} + | ^^^ not a trait + | +help: you might have meant to write a const parameter here + | +LL | fn make<const N: u32>() {} + | +++++ + +error[E0404]: expected trait, found builtin type `usize` + --> $DIR/change-ty-to-const-param-sugg-0.rs:5:17 + | +LL | struct Array<N: usize>([bool; N]); + | ^^^^^ not a trait + | +help: you might have meant to write a const parameter here + | +LL | struct Array<const N: usize>([bool; N]); + | +++++ + +error[E0423]: expected value, found type parameter `N` + --> $DIR/change-ty-to-const-param-sugg-0.rs:5:31 + | +LL | struct Array<N: usize>([bool; N]); + | - ^ not a value + | | + | found this type parameter + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0404, E0423. +For more information about an error, try `rustc --explain E0404`. diff --git a/tests/ui/resolve/change-ty-to-const-param-sugg-1.rs b/tests/ui/resolve/change-ty-to-const-param-sugg-1.rs new file mode 100644 index 00000000000..b1d2595f379 --- /dev/null +++ b/tests/ui/resolve/change-ty-to-const-param-sugg-1.rs @@ -0,0 +1,24 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +struct Tagged<T: Tag, O: Options>; +//~^ ERROR expected trait, found enum `Tag` +//~| HELP you might have meant to write a const parameter here +//~| ERROR expected trait, found struct `Options` +//~| HELP you might have meant to write a const parameter here + +#[derive(PartialEq, Eq, ConstParamTy)] +enum Tag { + One, + Two, +} + +#[derive(PartialEq, Eq, ConstParamTy)] +struct Options { + verbose: bool, + safe: bool, +} + +fn main() {} diff --git a/tests/ui/resolve/change-ty-to-const-param-sugg-1.stderr b/tests/ui/resolve/change-ty-to-const-param-sugg-1.stderr new file mode 100644 index 00000000000..933fac27f8f --- /dev/null +++ b/tests/ui/resolve/change-ty-to-const-param-sugg-1.stderr @@ -0,0 +1,25 @@ +error[E0404]: expected trait, found enum `Tag` + --> $DIR/change-ty-to-const-param-sugg-1.rs:6:18 + | +LL | struct Tagged<T: Tag, O: Options>; + | ^^^ not a trait + | +help: you might have meant to write a const parameter here + | +LL | struct Tagged<const T: Tag, O: Options>; + | +++++ + +error[E0404]: expected trait, found struct `Options` + --> $DIR/change-ty-to-const-param-sugg-1.rs:6:26 + | +LL | struct Tagged<T: Tag, O: Options>; + | ^^^^^^^ not a trait + | +help: you might have meant to write a const parameter here + | +LL | struct Tagged<T: Tag, const O: Options>; + | +++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0404`. |
