diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 51994c2e92f96..3a27962feca39 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -1,5 +1,4 @@ use std::collections::hash_map::Entry; -use std::fmt::Write; use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; @@ -124,13 +123,9 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { self.dcx().emit_err(ClobberAbiNotSupported { abi_span: *abi_span }); } Err(supported_abis) => { - let mut abis = format!("`{}`", supported_abis[0]); - for m in &supported_abis[1..] { - let _ = write!(abis, ", `{m}`"); - } self.dcx().emit_err(InvalidAbiClobberAbi { abi_span: *abi_span, - supported_abis: abis, + supported_abis: supported_abis.to_vec().into(), }); } } @@ -164,15 +159,12 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch { asm::InlineAsmRegClass::parse(asm_arch, reg_class).unwrap_or_else( |supported_register_classes| { - let mut register_classes = - format!("`{}`", supported_register_classes[0]); - for m in &supported_register_classes[1..] { - let _ = write!(register_classes, ", `{m}`"); - } self.dcx().emit_err(InvalidRegisterClass { op_span: *op_sp, reg_class, - supported_register_classes: register_classes, + supported_register_classes: supported_register_classes + .to_vec() + .into(), }); asm::InlineAsmRegClass::Err }, @@ -272,23 +264,20 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { } let valid_modifiers = class.valid_modifiers(asm_arch.unwrap()); if !valid_modifiers.contains(&modifier) { - let sub = if !valid_modifiers.is_empty() { - let mut mods = format!("`{}`", valid_modifiers[0]); - for m in &valid_modifiers[1..] { - let _ = write!(mods, ", `{m}`"); - } - InvalidAsmTemplateModifierRegClassSub::SupportModifier { + let sub = if valid_modifiers.is_empty() { + InvalidAsmTemplateModifierRegClassSub::DoesNotSupportModifier { class_name: class.name(), - modifiers: mods, } } else { - InvalidAsmTemplateModifierRegClassSub::DoesNotSupportModifier { + InvalidAsmTemplateModifierRegClassSub::SupportModifier { class_name: class.name(), + modifiers: valid_modifiers.to_vec().into(), } }; self.dcx().emit_err(InvalidAsmTemplateModifierRegClass { placeholder_span, op_span: op_sp, + modifier: modifier.to_string(), sub, }); } diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 1eb72727df667..95b8bb48c6a9c 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -1,5 +1,5 @@ -use rustc_errors::DiagArgFromDisplay; use rustc_errors::codes::*; +use rustc_errors::{DiagArgFromDisplay, DiagSymbolList}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; @@ -191,10 +191,10 @@ pub(crate) struct ClobberAbiNotSupported { #[derive(Diagnostic)] #[note("the following ABIs are supported on this target: {$supported_abis}")] #[diag("invalid ABI for `clobber_abi`")] -pub(crate) struct InvalidAbiClobberAbi { +pub(crate) struct InvalidAbiClobberAbi<'a> { #[primary_span] pub abi_span: Span, - pub supported_abis: String, + pub supported_abis: DiagSymbolList<&'a str>, } #[derive(Diagnostic)] @@ -215,17 +215,18 @@ pub(crate) struct InvalidRegisterClass { #[primary_span] pub op_span: Span, pub reg_class: Symbol, - pub supported_register_classes: String, + pub supported_register_classes: DiagSymbolList, } #[derive(Diagnostic)] -#[diag("invalid asm template modifier for this register class")] +#[diag("invalid asm template modifier `{$modifier}` for this register class")] pub(crate) struct InvalidAsmTemplateModifierRegClass { #[primary_span] #[label("template modifier")] pub placeholder_span: Span, #[label("argument")] pub op_span: Span, + pub modifier: String, #[subdiagnostic] pub sub: InvalidAsmTemplateModifierRegClassSub, } @@ -235,7 +236,7 @@ pub(crate) enum InvalidAsmTemplateModifierRegClassSub { #[note( "the `{$class_name}` register class supports the following template modifiers: {$modifiers}" )] - SupportModifier { class_name: Symbol, modifiers: String }, + SupportModifier { class_name: Symbol, modifiers: DiagSymbolList }, #[note("the `{$class_name}` register class does not support template modifiers")] DoesNotSupportModifier { class_name: Symbol }, } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a72ed842b8e35..050c20031cd5d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1786,6 +1786,23 @@ impl<'a> State<'a> { } } + /// Print a pattern, parenthesizing it if it is an or-pattern (`A | B`). + /// + /// Or-patterns have the lowest precedence among patterns, so they need + /// parentheses when nested inside `@` bindings, `&` references, or `box` + /// patterns — otherwise `x @ A | B` parses as `(x @ A) | B`, `&A | B` + /// parses as `(&A) | B`, etc. + fn print_pat_paren_if_or(&mut self, pat: &ast::Pat) { + let needs_paren = matches!(pat.kind, PatKind::Or(..)); + if needs_paren { + self.popen(); + } + self.print_pat(pat); + if needs_paren { + self.pclose(); + } + } + fn print_pat(&mut self, pat: &ast::Pat) { self.maybe_print_comment(pat.span.lo()); self.ann.pre(self, AnnNode::Pat(pat)); @@ -1813,7 +1830,7 @@ impl<'a> State<'a> { if let Some(p) = sub { self.space(); self.word_space("@"); - self.print_pat(p); + self.print_pat_paren_if_or(p); } } PatKind::TupleStruct(qself, path, elts) => { @@ -1885,7 +1902,7 @@ impl<'a> State<'a> { } PatKind::Box(inner) => { self.word("box "); - self.print_pat(inner); + self.print_pat_paren_if_or(inner); } PatKind::Deref(inner) => { self.word("deref!"); @@ -1909,7 +1926,7 @@ impl<'a> State<'a> { self.print_pat(inner); self.pclose(); } else { - self.print_pat(inner); + self.print_pat_paren_if_or(inner); } } PatKind::Expr(e) => self.print_expr(e, FixupContext::default()), diff --git a/compiler/rustc_attr_parsing/src/errors.rs b/compiler/rustc_attr_parsing/src/errors.rs new file mode 100644 index 0000000000000..4eb9425b34bb0 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/errors.rs @@ -0,0 +1,35 @@ +use rustc_errors::MultiSpan; +use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_span::{Span, Symbol}; + +#[derive(Diagnostic)] +#[diag("`{$name}` attribute cannot be used at crate level")] +pub(crate) struct InvalidAttrAtCrateLevel { + #[primary_span] + pub span: Span, + #[suggestion( + "perhaps you meant to use an outer attribute", + code = "#[", + applicability = "machine-applicable", + style = "verbose" + )] + pub pound_to_opening_bracket: Span, + pub name: Symbol, + #[subdiagnostic] + pub item: Option, +} + +#[derive(Clone, Copy, Subdiagnostic)] +#[label("the inner attribute doesn't annotate this item")] +pub(crate) struct ItemFollowingInnerAttr { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag("most attributes are not supported in `where` clauses")] +#[help("only `#[cfg]` and `#[cfg_attr]` are supported")] +pub(crate) struct UnsupportedAttributesInWhere { + #[primary_span] + pub span: MultiSpan, +} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index fa66dec6a1568..7305c4b7c2fa8 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -269,6 +269,11 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { mut emit_lint: impl FnMut(LintId, Span, AttributeLintKind), ) -> Vec { let mut attributes = Vec::new(); + // We store the attributes we intend to discard at the end of this function in order to + // check they are applied to the right target and error out if necessary. In practice, we + // end up dropping only derive attributes and derive helpers, both being fully processed + // at macro expansion. + let mut dropped_attributes = Vec::new(); let mut attr_paths: Vec> = Vec::new(); let mut early_parsed_state = EarlyParsedState::default(); @@ -304,7 +309,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { kind: DocFragmentKind::Sugared(*comment_kind), span: attr_span, comment: *symbol, - })) + })); } ast::AttrKind::Normal(n) => { attr_paths.push(PathParser(&n.item.path)); @@ -393,29 +398,33 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { Self::check_target(&accept.allowed_targets, target, &mut cx); } } else { - // If we're here, we must be compiling a tool attribute... Or someone - // forgot to parse their fancy new attribute. Let's warn them in any case. - // If you are that person, and you really think your attribute should - // remain unparsed, carefully read the documentation in this module and if - // you still think so you can add an exception to this assertion. - - // FIXME(jdonszelmann): convert other attributes, and check with this that - // we caught em all - // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg]; - // assert!( - // self.tools.contains(&parts[0]) || true, - // // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]), - // "attribute {path} wasn't parsed and isn't a know tool attribute", - // ); - - attributes.push(Attribute::Unparsed(Box::new(AttrItem { + let attr = AttrItem { path: attr_path.clone(), args: self .lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span), id: HashIgnoredAttrId { attr_id: attr.id }, style: attr.style, span: attr_span, - }))); + }; + + if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) + && target == Target::Crate + { + self.check_invalid_crate_level_attr_item(&attr, n.item.span()); + } + + let attr = Attribute::Unparsed(Box::new(attr)); + + if self.tools.contains(&parts[0]) + // FIXME: this can be removed once #152369 has been merged. + // https://github.com/rust-lang/rust/pull/152369 + || [sym::allow, sym::deny, sym::expect, sym::forbid, sym::warn] + .contains(&parts[0]) + { + attributes.push(attr); + } else { + dropped_attributes.push(attr); + } } } } @@ -431,6 +440,12 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } } + if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) + && target == Target::WherePredicate + { + self.check_invalid_where_predicate_attrs(attributes.iter().chain(&dropped_attributes)); + } + attributes } diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index db09572cc56b3..93eb5a0c3ab73 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -79,6 +79,7 @@ // tidy-alphabetical-start #![feature(decl_macro)] #![feature(iter_intersperse)] +#![feature(try_blocks)] #![recursion_limit = "256"] // tidy-alphabetical-end @@ -99,6 +100,7 @@ mod interface; pub mod parser; mod early_parsed; +mod errors; mod safety; mod session_diagnostics; mod target_checking; diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index fb005477f0fa1..253a089e49f1a 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -1,14 +1,18 @@ use std::borrow::Cow; use rustc_ast::AttrStyle; -use rustc_errors::DiagArgValue; +use rustc_errors::{DiagArgValue, MultiSpan, StashKey}; use rustc_feature::Features; +use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLintKind; -use rustc_hir::{MethodKind, Target}; -use rustc_span::sym; +use rustc_hir::{AttrItem, Attribute, MethodKind, Target}; +use rustc_span::{BytePos, Span, Symbol, sym}; use crate::AttributeParser; use crate::context::{AcceptContext, Stage}; +use crate::errors::{ + InvalidAttrAtCrateLevel, ItemFollowingInnerAttr, UnsupportedAttributesInWhere, +}; use crate::session_diagnostics::InvalidTarget; use crate::target_checking::Policy::Allow; @@ -96,6 +100,25 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { return; } + if matches!(cx.attr_path.segments.as_ref(), [sym::repr]) && target == Target::Crate { + // The allowed targets of `repr` depend on its arguments. They can't be checked using + // the `AttributeParser` code. + let span = cx.attr_span; + let item = + cx.cx.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span }); + + let pound_to_opening_bracket = cx.attr_span.until(cx.inner_span); + + cx.dcx() + .create_err(InvalidAttrAtCrateLevel { + span, + pound_to_opening_bracket, + name: sym::repr, + item, + }) + .emit(); + } + match allowed_targets.is_allowed(target) { AllowedResult::Allowed => {} AllowedResult::Warn => { @@ -163,6 +186,113 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span); } + + // FIXME: Fix "Cannot determine resolution" error and remove built-in macros + // from this check. + pub(crate) fn check_invalid_crate_level_attr_item(&self, attr: &AttrItem, inner_span: Span) { + // Check for builtin attributes at the crate level + // which were unsuccessfully resolved due to cannot determine + // resolution for the attribute macro error. + const ATTRS_TO_CHECK: &[Symbol] = + &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench]; + + // FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day. + if let Some(name) = ATTRS_TO_CHECK.iter().find(|attr_to_check| matches!(attr.path.segments.as_ref(), [segment] if segment == *attr_to_check)) { + let span = attr.span; + let name = *name; + + let item = self.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span }); + + let err = self.dcx().create_err(InvalidAttrAtCrateLevel { + span, + pound_to_opening_bracket: span.until(inner_span), + name, + item, + }); + + self.dcx().try_steal_replace_and_emit_err( + attr.path.span, + StashKey::UndeterminedMacroResolution, + err, + ); + } + } + + fn first_line_of_next_item(&self, span: Span) -> Option { + // We can't exactly call `tcx.hir_free_items()` here because it's too early and querying + // this would create a circular dependency. Instead, we resort to getting the original + // source code that follows `span` and find the next item from here. + + self.sess() + .source_map() + .span_to_source(span, |content, _, span_end| { + let mut source = &content[span_end..]; + let initial_source_len = source.len(); + let span = try { + loop { + let first = source.chars().next()?; + + if first.is_whitespace() { + let split_idx = source.find(|c: char| !c.is_whitespace())?; + source = &source[split_idx..]; + } else if source.starts_with("//") { + let line_idx = source.find('\n')?; + source = &source[line_idx + '\n'.len_utf8()..]; + } else if source.starts_with("/*") { + // FIXME: support nested comments. + let close_idx = source.find("*/")?; + source = &source[close_idx + "*/".len()..]; + } else if first == '#' { + // FIXME: properly find the end of the attributes in order to accurately + // skip them. This version just consumes the source code until the next + // `]`. + let close_idx = source.find(']')?; + source = &source[close_idx + ']'.len_utf8()..]; + } else { + let lo = span_end + initial_source_len - source.len(); + let last_line = source.split('\n').next().map(|s| s.trim_end())?; + + let hi = lo + last_line.len(); + let lo = BytePos(lo as u32); + let hi = BytePos(hi as u32); + let next_item_span = Span::new(lo, hi, span.ctxt(), None); + + break next_item_span; + } + } + }; + + Ok(span) + }) + .ok() + .flatten() + } + + pub(crate) fn check_invalid_where_predicate_attrs<'attr>( + &self, + attrs: impl IntoIterator, + ) { + // FIXME(where_clause_attrs): Currently, as the following check shows, + // only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed + // if we allow more attributes (e.g., tool attributes and `allow/deny/warn`) + // in where clauses. After that, this function would become useless. + let spans = attrs + .into_iter() + // FIXME: We shouldn't need to special-case `doc`! + .filter(|attr| { + matches!( + attr, + Attribute::Parsed(AttributeKind::DocComment { .. } | AttributeKind::Doc(_)) + | Attribute::Unparsed(_) + ) + }) + .map(|attr| attr.span()) + .collect::>(); + if !spans.is_empty() { + self.dcx() + .emit_err(UnsupportedAttributesInWhere { span: MultiSpan::from_spans(spans) }); + } + } } /// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to. diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 45e16a735fed6..466f6c094e2a1 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -252,7 +252,10 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::new("fullfp16")), s => Some(LLVMFeature::new(s)), }, - + Arch::Bpf => match s { + "allows-misaligned-mem-access" if major < 22 => None, + s => Some(LLVMFeature::new(s)), + }, // Filter out features that are not supported by the current LLVM version Arch::PowerPC | Arch::PowerPC64 => match s { "power8-crypto" => Some(LLVMFeature::new("crypto")), diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 41a4cb2f23a1c..6466d533421bc 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -128,7 +128,6 @@ enum ExpectedKind { Reference, Box, RawPtr, - InitScalar, Bool, Char, Float, @@ -143,7 +142,6 @@ impl fmt::Display for ExpectedKind { ExpectedKind::Reference => "expected a reference", ExpectedKind::Box => "expected a box", ExpectedKind::RawPtr => "expected a raw pointer", - ExpectedKind::InitScalar => "expected initialized scalar value", ExpectedKind::Bool => "expected a boolean", ExpectedKind::Char => "expected a unicode scalar value", ExpectedKind::Float => "expected a floating point number", @@ -1478,7 +1476,9 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, BackendRepr::Scalar(scalar_layout) => { if !scalar_layout.is_uninit_valid() { // There is something to check here. - let scalar = self.read_scalar(val, ExpectedKind::InitScalar)?; + // We read directly via `ecx` since the read cannot fail -- we already read + // this field above when recursing into the field. + let scalar = self.ecx.read_scalar(val)?; self.visit_scalar(scalar, scalar_layout)?; } } @@ -1487,8 +1487,9 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // FIXME: find a way to also check ScalarPair when one side can be uninit but // the other must be init. if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() { - let (a, b) = - self.read_immediate(val, ExpectedKind::InitScalar)?.to_scalar_pair(); + // We read directly via `ecx` since the read cannot fail -- we already read + // this field above when recursing into the field. + let (a, b) = self.ecx.read_immediate(val)?.to_scalar_pair(); self.visit_scalar(a, a_layout)?; self.visit_scalar(b, b_layout)?; } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 893cd025a089d..3198bbe0bbc67 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2291,8 +2291,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_sig, ); let name = inherent_method.name(); + let inputs = fn_sig.inputs(); + let expected_inputs = + if inherent_method.is_method() { &inputs[1..] } else { inputs }; if let Some(ref args) = call_args - && fn_sig.inputs()[1..] + && expected_inputs .iter() .eq_by(args, |expected, found| self.may_coerce(*expected, *found)) { diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index dcf7a952fb970..7d94d60523fb1 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -104,9 +104,6 @@ pub struct QueryVTable<'tcx, C: QueryCache> { index: DepNodeIndex, ) -> Option, - pub is_loadable_from_disk_fn: - fn(tcx: TyCtxt<'tcx>, key: C::Key, index: SerializedDepNodeIndex) -> bool, - /// Function pointer that hashes this query's result values. /// /// For `no_hash` queries, this function pointer is None. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a8ca8011b5710..26ba2b0e8f42d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -15,7 +15,7 @@ use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey, msg}; +use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, msg}; use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_hir::attrs::diagnostic::Directive; use rustc_hir::attrs::{ @@ -45,7 +45,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; -use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; use rustc_trait_selection::traits::ObligationCtxt; @@ -1907,27 +1907,6 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { } fn visit_where_predicate(&mut self, where_predicate: &'tcx hir::WherePredicate<'tcx>) { - // FIXME(where_clause_attrs): Currently, as the following check shows, - // only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed - // if we allow more attributes (e.g., tool attributes and `allow/deny/warn`) - // in where clauses. After that, only `self.check_attributes` should be enough. - let spans = self - .tcx - .hir_attrs(where_predicate.hir_id) - .iter() - // FIXME: We shouldn't need to special-case `doc`! - .filter(|attr| { - matches!( - attr, - Attribute::Parsed(AttributeKind::DocComment { .. } | AttributeKind::Doc(_)) - | Attribute::Unparsed(_) - ) - }) - .map(|attr| attr.span()) - .collect::>(); - if !spans.is_empty() { - self.tcx.dcx().emit_err(errors::UnsupportedAttributesInWhere { span: spans.into() }); - } self.check_attributes( where_predicate.hir_id, where_predicate.span, @@ -2025,64 +2004,6 @@ fn is_c_like_enum(item: &Item<'_>) -> bool { } } -// FIXME: Fix "Cannot determine resolution" error and remove built-in macros -// from this check. -fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { - // Check for builtin attributes at the crate level - // which were unsuccessfully resolved due to cannot determine - // resolution for the attribute macro error. - const ATTRS_TO_CHECK: &[Symbol] = - &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench]; - - for attr in attrs { - // FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day. - let (span, name) = if let Some(a) = - ATTRS_TO_CHECK.iter().find(|attr_to_check| attr.has_name(**attr_to_check)) - { - (attr.span(), *a) - } else if let Attribute::Parsed(AttributeKind::Repr { - reprs: _, - first_span: first_attr_span, - }) = attr - { - (*first_attr_span, sym::repr) - } else { - continue; - }; - - let item = tcx - .hir_free_items() - .map(|id| tcx.hir_item(id)) - .find(|item| !item.span.is_dummy()) // Skip prelude `use`s - .map(|item| errors::ItemFollowingInnerAttr { - span: if let Some(ident) = item.kind.ident() { ident.span } else { item.span }, - kind: tcx.def_descr(item.owner_id.to_def_id()), - }); - let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel { - span, - sugg_span: tcx - .sess - .source_map() - .span_to_snippet(span) - .ok() - .filter(|src| src.starts_with("#![")) - .map(|_| span.with_lo(span.lo() + BytePos(1)).with_hi(span.lo() + BytePos(2))), - name, - item, - }); - - if let Attribute::Unparsed(p) = attr { - tcx.dcx().try_steal_replace_and_emit_err( - p.path.span, - StashKey::UndeterminedMacroResolution, - err, - ); - } else { - err.emit(); - } - } -} - fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) { let attrs = tcx.hir_attrs(item.hir_id()); @@ -2098,7 +2019,6 @@ fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { tcx.hir_visit_item_likes_in_module(module_def_id, check_attr_visitor); if module_def_id.to_local_def_id().is_top_level_module() { check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None); - check_invalid_crate_level_attr(tcx, tcx.hir_krate_attrs()); } if check_attr_visitor.abort.get() { tcx.dcx().abort_if_errors() diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 5e84122fcb10e..f9dc696f320e3 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -3,8 +3,7 @@ use std::path::{Path, PathBuf}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, - MultiSpan, msg, + Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, MultiSpan, msg, }; use rustc_hir::Target; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -447,44 +446,6 @@ pub(crate) struct LangItemOnIncorrectTarget { pub actual_target: Target, } -pub(crate) struct InvalidAttrAtCrateLevel { - pub span: Span, - pub sugg_span: Option, - pub name: Symbol, - pub item: Option, -} - -#[derive(Clone, Copy)] -pub(crate) struct ItemFollowingInnerAttr { - pub span: Span, - pub kind: &'static str, -} - -impl Diagnostic<'_, G> for InvalidAttrAtCrateLevel { - #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - let mut diag = - Diag::new(dcx, level, msg!("`{$name}` attribute cannot be used at crate level")); - diag.span(self.span); - diag.arg("name", self.name); - // Only emit an error with a suggestion if we can create a string out - // of the attribute span - if let Some(span) = self.sugg_span { - diag.span_suggestion_verbose( - span, - msg!("perhaps you meant to use an outer attribute"), - String::new(), - Applicability::MachineApplicable, - ); - } - if let Some(item) = self.item { - diag.arg("kind", item.kind); - diag.span_label(item.span, msg!("the inner attribute doesn't annotate this {$kind}")); - } - diag - } -} - #[derive(Diagnostic)] #[diag("duplicate diagnostic item in crate `{$crate_name}`: `{$name}`")] pub(crate) struct DuplicateDiagnosticItemInCrate { @@ -1189,14 +1150,6 @@ pub(crate) struct RustcConstStableIndirectPairing { pub span: Span, } -#[derive(Diagnostic)] -#[diag("most attributes are not supported in `where` clauses")] -#[help("only `#[cfg]` and `#[cfg_attr]` are supported")] -pub(crate) struct UnsupportedAttributesInWhere { - #[primary_span] - pub span: MultiSpan, -} - #[derive(Diagnostic)] pub(crate) enum UnexportableItem<'a> { #[diag("{$descr}'s are not exportable")] diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index bbd55e9ead119..78ead0cf14f1b 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -18,7 +18,7 @@ use tracing::warn; use crate::dep_graph::{DepNode, DepNodeIndex}; use crate::job::{QueryJobInfo, QueryJobMap, find_cycle_in_stack, report_cycle}; -use crate::plumbing::{current_query_job, next_job_id, start_query}; +use crate::plumbing::{current_query_job, loadable_from_disk, next_job_id, start_query}; use crate::query_impl::for_each_query_vtable; #[inline] @@ -488,81 +488,59 @@ fn load_from_disk_or_invoke_provider_green<'tcx, C: QueryCache>( debug_assert!(dep_graph_data.is_index_green(prev_index)); - // First we try to load the result from the on-disk cache. - // Some things are never cached on disk. - if let Some(value) = (query.try_load_from_disk_fn)(tcx, key, prev_index, dep_node_index) { - if std::intrinsics::unlikely(tcx.sess.opts.unstable_opts.query_dep_graph) { - dep_graph_data.mark_debug_loaded_from_disk(*dep_node) + // First try to load the result from the on-disk cache. Some things are never cached on disk. + let value; + let verify; + match (query.try_load_from_disk_fn)(tcx, key, prev_index, dep_node_index) { + Some(loaded_value) => { + if std::intrinsics::unlikely(tcx.sess.opts.unstable_opts.query_dep_graph) { + dep_graph_data.mark_debug_loaded_from_disk(*dep_node) + } + + value = loaded_value; + + let prev_fingerprint = dep_graph_data.prev_value_fingerprint_of(prev_index); + // If `-Zincremental-verify-ich` is specified, re-hash results from + // the cache and make sure that they have the expected fingerprint. + // + // If not, we still seek to verify a subset of fingerprints loaded + // from disk. Re-hashing results is fairly expensive, so we can't + // currently afford to verify every hash. This subset should still + // give us some coverage of potential bugs. + verify = prev_fingerprint.split().1.as_u64().is_multiple_of(32) + || tcx.sess.opts.unstable_opts.incremental_verify_ich; } + None => { + // We could not load a result from the on-disk cache, so recompute. The dep-graph for + // this computation is already in-place, so we can just call the query provider. + let prof_timer = tcx.prof.query_provider(); + value = tcx.dep_graph.with_ignore(|| (query.invoke_provider_fn)(tcx, key)); + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - let prev_fingerprint = dep_graph_data.prev_value_fingerprint_of(prev_index); - // If `-Zincremental-verify-ich` is specified, re-hash results from - // the cache and make sure that they have the expected fingerprint. - // - // If not, we still seek to verify a subset of fingerprints loaded - // from disk. Re-hashing results is fairly expensive, so we can't - // currently afford to verify every hash. This subset should still - // give us some coverage of potential bugs though. - let try_verify = prev_fingerprint.split().1.as_u64().is_multiple_of(32); - if std::intrinsics::unlikely( - try_verify || tcx.sess.opts.unstable_opts.incremental_verify_ich, - ) { - incremental_verify_ich( - tcx, - dep_graph_data, - &value, - prev_index, - query.hash_value_fn, - query.format_value, - ); + verify = true; } + }; - return value; + if verify { + // Verify that re-running the query produced a result with the expected hash. + // This catches bugs in query implementations, turning them into ICEs. + // For example, a query might sort its result by `DefId` - since `DefId`s are + // not stable across compilation sessions, the result could get up getting sorted + // in a different order when the query is re-run, even though all of the inputs + // (e.g. `DefPathHash` values) were green. + // + // See issue #82920 for an example of a miscompilation that would get turned into + // an ICE by this check + incremental_verify_ich( + tcx, + dep_graph_data, + &value, + prev_index, + query.hash_value_fn, + query.format_value, + ); } - // We always expect to find a cached result for things that - // can be forced from `DepNode`. - debug_assert!( - !(query.will_cache_on_disk_for_key_fn)(tcx, key) - || !tcx.key_fingerprint_style(dep_node.kind).is_maybe_recoverable(), - "missing on-disk cache entry for {dep_node:?}" - ); - - // Sanity check for the logic in `ensure`: if the node is green and the result loadable, - // we should actually be able to load it. - debug_assert!( - !(query.is_loadable_from_disk_fn)(tcx, key, prev_index), - "missing on-disk cache entry for loadable {dep_node:?}" - ); - - // We could not load a result from the on-disk cache, so - // recompute. - let prof_timer = tcx.prof.query_provider(); - - // The dep-graph for this computation is already in-place. - // Call the query provider. - let value = tcx.dep_graph.with_ignore(|| (query.invoke_provider_fn)(tcx, key)); - - prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - - // Verify that re-running the query produced a result with the expected hash - // This catches bugs in query implementations, turning them into ICEs. - // For example, a query might sort its result by `DefId` - since `DefId`s are - // not stable across compilation sessions, the result could get up getting sorted - // in a different order when the query is re-run, even though all of the inputs - // (e.g. `DefPathHash` values) were green. - // - // See issue #82920 for an example of a miscompilation that would get turned into - // an ICE by this check - incremental_verify_ich( - tcx, - dep_graph_data, - &value, - prev_index, - query.hash_value_fn, - query.format_value, - ); - value } @@ -624,7 +602,8 @@ fn check_if_ensure_can_skip_execution<'tcx, C: QueryCache>( // In ensure-done mode, we can only skip execution for this key if // there's a disk-cached value available to load later if needed, // which guarantees the query provider will never run for this key. - let is_loadable = (query.is_loadable_from_disk_fn)(tcx, key, serialized_dep_node_index); + let is_loadable = (query.will_cache_on_disk_for_key_fn)(tcx, key) + && loadable_from_disk(tcx, serialized_dep_node_index); EnsureCanSkip { skip_execution: is_loadable, dep_node: Some(dep_node) } } } diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index 56b7534da9edf..1a8ed053e347a 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -165,14 +165,6 @@ macro_rules! define_queries { #[cfg(not($cache_on_disk))] try_load_from_disk_fn: |_tcx, _key, _prev_index, _index| None, - #[cfg($cache_on_disk)] - is_loadable_from_disk_fn: |tcx, key, index| -> bool { - rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) && - $crate::plumbing::loadable_from_disk(tcx, index) - }, - #[cfg(not($cache_on_disk))] - is_loadable_from_disk_fn: |_tcx, _key, _index| false, - // The default just emits `err` and then aborts. // `from_cycle_error::specialize_query_vtables` overwrites this default for // certain queries. diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 80674ab4cf8a2..18dfd11332d02 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -756,8 +756,10 @@ static WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; -const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] = - &[("alu32", Unstable(sym::bpf_target_feature), &[])]; +const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + ("alu32", Unstable(sym::bpf_target_feature), &[]), + ("allows-misaligned-mem-access", Unstable(sym::bpf_target_feature), &[]), +]; static CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 6bf08170bcf7e..eeccf1eb0ecfa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2299,6 +2299,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // FIXME: this could use a better heuristic, like just checking // that args[1..] is the same. let all_traits_equal = traits.len() == 1; + let mut types: Vec<_> = + candidates.iter().map(|(c, _)| c.self_ty().to_string()).collect(); + types.sort(); + types.dedup(); + let all_types_equal = types.len() == 1; let end = if candidates.len() <= 9 || self.tcx.sess.opts.verbose { candidates.len() @@ -2312,6 +2317,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { for (c, def_id) in &candidates { let msg = if all_traits_equal { format!("`{}`", self.tcx.short_string(c.self_ty(), err.long_ty_path())) + } else if all_types_equal { + format!( + "`{}`", + self.tcx.short_string(c.print_only_trait_path(), err.long_ty_path()) + ) } else { format!( "`{}` implements `{}`", @@ -2321,13 +2331,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; span.push_span_label(self.tcx.def_span(*def_id), msg); } - err.span_help( - span, + let msg = if all_types_equal { + format!( + "`{}` implements trait `{}`", + self.tcx.short_string(candidates[0].0.self_ty(), err.long_ty_path()), + self.tcx.short_string(trait_ref.print_trait_sugared(), err.long_ty_path()), + ) + } else { format!( "the following {other}types implement trait `{}`", - trait_ref.print_trait_sugared(), - ), - ); + self.tcx.short_string(trait_ref.print_trait_sugared(), err.long_ty_path()), + ) + }; + err.span_help(span, msg); } else { let candidate_names: Vec = candidates .iter() @@ -2337,6 +2353,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { "\n {}", self.tcx.short_string(c.self_ty(), err.long_ty_path()) ) + } else if all_types_equal { + format!( + "\n {}", + self.tcx + .short_string(c.print_only_trait_path(), err.long_ty_path()) + ) } else { format!( "\n `{}` implements `{}`", @@ -2347,9 +2369,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } }) .collect(); + let msg = if all_types_equal { + format!( + "`{}` implements trait `{}`", + self.tcx.short_string(candidates[0].0.self_ty(), err.long_ty_path()), + self.tcx.short_string(trait_ref.print_trait_sugared(), err.long_ty_path()), + ) + } else { + format!( + "the following {other}types implement trait `{}`", + self.tcx.short_string(trait_ref.print_trait_sugared(), err.long_ty_path()), + ) + }; + err.help(format!( - "the following {other}types implement trait `{}`:{}{}", - trait_ref.print_trait_sugared(), + "{msg}:{}{}", candidate_names[..end].join(""), if candidates.len() > 9 && !self.tcx.sess.opts.verbose { format!("\nand {} others", candidates.len() - 8) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 68e4f1c2aa787..128df654e078d 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2502,14 +2502,6 @@ pub(crate) macro const_eval_select { /// particular value, ever. However, the compiler will generally make it /// return `true` only if the value of the argument is actually known. /// -/// # Stability concerns -/// -/// While it is safe to call, this intrinsic may behave differently in -/// a `const` context than otherwise. See the [`const_eval_select()`] -/// documentation for an explanation of the issues this can cause. Unlike -/// `const_eval_select`, this intrinsic isn't guaranteed to behave -/// deterministically even in a `const` context. -/// /// # Type Requirements /// /// `T` must be either a `bool`, a `char`, a primitive numeric type (e.g. `f32`, @@ -2590,6 +2582,12 @@ pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { /// assertions are enabled whenever the *user crate* has UB checks enabled. However, if the /// user has UB checks disabled, the checks will still get optimized out. This intrinsic is /// primarily used by [`crate::ub_checks::assert_unsafe_precondition`]. +/// +/// # Consteval +/// +/// In consteval, this function currently returns `true`. This is because the value of the `ub_checks` +/// configuration can differ across crates, but we need this function to always return the same +/// value in consteval in order to avoid unsoundness. #[rustc_intrinsic_const_stable_indirect] // just for UB checks #[inline(always)] #[rustc_intrinsic] @@ -2609,6 +2607,12 @@ pub const fn ub_checks() -> bool { /// `#[inline]`), gating assertions on `overflow_checks()` rather than `cfg!(overflow_checks)` means that /// assertions are enabled whenever the *user crate* has overflow checks enabled. However if the /// user has overflow checks disabled, the checks will still get optimized out. +/// +/// # Consteval +/// +/// In consteval, this function currently returns `true`. This is because the value of the `overflow_checks` +/// configuration can differ across crates, but we need this function to always return the same +/// value in consteval in order to avoid unsoundness. #[inline(always)] #[rustc_intrinsic] pub const fn overflow_checks() -> bool { diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index c1d4fbbd23ad4..23ace6e1ba253 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -298,9 +298,9 @@ range_incl_exact_iter_impl! { #[derive(Debug, Clone)] pub struct RangeFromIter { start: A, - /// Whether the first element of the iterator has yielded. + /// Whether the maximum value of the iterator has yielded. /// Only used when overflow checks are enabled. - first: bool, + exhausted: bool, } impl RangeFromIter { @@ -309,10 +309,12 @@ impl RangeFromIter { #[rustc_inherit_overflow_checks] #[unstable(feature = "new_range_api", issue = "125687")] pub fn remainder(self) -> RangeFrom { - if intrinsics::overflow_checks() { - if !self.first { - return RangeFrom { start: Step::forward(self.start, 1) }; - } + // Need to handle this case even if overflow-checks are disabled, + // because a `RangeFromIter` could be exhausted in a crate with + // overflow-checks enabled, but then passed to a crate with them + // disabled before this is called. + if self.exhausted { + return RangeFrom { start: Step::forward(self.start, 1) }; } RangeFrom { start: self.start } @@ -326,14 +328,29 @@ impl Iterator for RangeFromIter { #[inline] #[rustc_inherit_overflow_checks] fn next(&mut self) -> Option { + if self.exhausted { + // This should panic if overflow checks are enabled, since + // `forward_checked` returned `None` in prior iteration. + self.start = Step::forward(self.start.clone(), 1); + + // If we get here, if means this iterator was exhausted by a crate + // with overflow-checks enabled, but now we're iterating in a crate with + // overflow-checks disabled. Since we successfully incremented `self.start` + // above (in many cases this will wrap around to MIN), we now unset + // the flag so we don't repeat this process in the next iteration. + // + // This could also happen if `forward_checked` returned None but + // (for whatever reason, not applicable to any std implementors) + // `forward` doesn't panic when overflow-checks are enabled. In that + // case, this is also the correct behavior. + self.exhausted = false; + } if intrinsics::overflow_checks() { - if self.first { - self.first = false; + let Some(n) = Step::forward_checked(self.start.clone(), 1) else { + self.exhausted = true; return Some(self.start.clone()); - } - - self.start = Step::forward(self.start.clone(), 1); - return Some(self.start.clone()); + }; + return Some(mem::replace(&mut self.start, n)); } let n = Step::forward(self.start.clone(), 1); @@ -348,18 +365,22 @@ impl Iterator for RangeFromIter { #[inline] #[rustc_inherit_overflow_checks] fn nth(&mut self, n: usize) -> Option { + // Typically `forward` will cause an overflow-check panic here, + // but unset the exhausted flag to handle the uncommon cases. + // See the comments in `next` for more details. + if self.exhausted { + self.start = Step::forward(self.start.clone(), 1); + self.exhausted = false; + } if intrinsics::overflow_checks() { - if self.first { - self.first = false; - - let plus_n = Step::forward(self.start.clone(), n); + let plus_n = Step::forward(self.start.clone(), n); + if let Some(plus_n1) = Step::forward_checked(plus_n.clone(), 1) { + self.start = plus_n1; + } else { self.start = plus_n.clone(); - return Some(plus_n); + self.exhausted = true; } - - let plus_n = Step::forward(self.start.clone(), n); - self.start = Step::forward(plus_n.clone(), 1); - return Some(self.start.clone()); + return Some(plus_n); } let plus_n = Step::forward(self.start.clone(), n); @@ -380,6 +401,6 @@ impl IntoIterator for RangeFrom { type IntoIter = RangeFromIter; fn into_iter(self) -> Self::IntoIter { - RangeFromIter { start: self.start, first: true } + RangeFromIter { start: self.start, exhausted: false } } } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 1b7a41d697367..7a8ccdbc5043b 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -62,7 +62,7 @@ path = "../windows_link" rand = { version = "0.9.0", default-features = false, features = ["alloc"] } rand_xorshift = "0.4.0" -[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", target_os = "vexos", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] +[target.'cfg(any(all(target_family = "wasm", not(any(unix, target_os = "wasi"))), target_os = "xous", target_os = "vexos", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] dlmalloc = { version = "0.2.10", features = ['rustc-dep-of-std'] } [target.x86_64-fortanix-unknown-sgx.dependencies] diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index f1303814f6d72..6af11d9ae0a81 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -883,6 +883,12 @@ impl Build { features.push("check_only"); } + if crates.iter().any(|c| c == "rustc_transmute") { + // for `x test rustc_transmute`, this feature isn't enabled automatically by a + // dependent crate. + features.push("rustc"); + } + // If debug logging is on, then we want the default for tracing: // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26 // which is everything (including debug/trace/etc.) diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed index 2c794463457fb..852c4ddcfd712 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed +++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed @@ -23,6 +23,6 @@ fn main() { //~^ unnested_or_patterns if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} //~^ unnested_or_patterns - if let box box (0 | 2 | 4) = Box::new(Box::new(0)) {} + if let box (box (0 | 2 | 4)) = Box::new(Box::new(0)) {} //~^ unnested_or_patterns } diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr index 3fc8fa174c939..3deef33cf258e 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr +++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr @@ -93,7 +93,7 @@ LL | if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {} help: nest the patterns | LL - if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {} -LL + if let box box (0 | 2 | 4) = Box::new(Box::new(0)) {} +LL + if let box (box (0 | 2 | 4)) = Box::new(Box::new(0)) {} | error: aborting due to 8 previous errors diff --git a/src/tools/rust-analyzer/README.md b/src/tools/rust-analyzer/README.md index 6d2a673286853..06d63a2d23a73 100644 --- a/src/tools/rust-analyzer/README.md +++ b/src/tools/rust-analyzer/README.md @@ -20,6 +20,8 @@ analyzing Rust code. See [Architecture](https://rust-analyzer.github.io/book/contributing/architecture.html) in the manual. +[![codecov](https://codecov.io/github/rust-lang/rust-analyzer/graph/badge.svg)](https://app.codecov.io/github/rust-lang/rust-analyzer/tree/master) + ## Quick Start https://rust-analyzer.github.io/book/installation.html diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs index 998aab5a3fff0..65931549db02f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs @@ -1,7 +1,5 @@ //! Things related to IR printing in the next-trait-solver. -use std::any::type_name_of_val; - use rustc_type_ir::{self as ty, ir_print::IrPrint}; use super::SolverDefId; @@ -82,7 +80,10 @@ impl<'db> IrPrint> for DbInterner<'db> { t: &ty::TraitPredicate, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + match t.polarity { + ty::PredicatePolarity::Positive => write!(fmt, "{:?}", t.trait_ref), + ty::PredicatePolarity::Negative => write!(fmt, "!{:?}", t.trait_ref), + } } } impl<'db> IrPrint> for DbInterner<'db> { @@ -97,7 +98,11 @@ impl<'db> IrPrint> for DbInterner<'db> t: &rustc_type_ir::HostEffectPredicate, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + let prefix = match t.constness { + ty::BoundConstness::Const => "const", + ty::BoundConstness::Maybe => "[const]", + }; + write!(fmt, "{prefix} {:?}", t.trait_ref) } } impl<'db> IrPrint> for DbInterner<'db> { @@ -183,7 +188,7 @@ impl<'db> IrPrint> for DbInterner<'db> { t: &ty::NormalizesTo, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + write!(fmt, "NormalizesTo({} -> {:?})", t.alias, t.term) } } impl<'db> IrPrint> for DbInterner<'db> { @@ -198,7 +203,7 @@ impl<'db> IrPrint> for DbInterner<'db> { t: &ty::SubtypePredicate, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + write!(fmt, "{:?} <: {:?}", t.a, t.b) } } impl<'db> IrPrint> for DbInterner<'db> { @@ -210,7 +215,7 @@ impl<'db> IrPrint> for DbInterner<'db> { t: &ty::CoercePredicate, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + write!(fmt, "CoercePredicate({:?} -> {:?})", t.a, t.b) } } impl<'db> IrPrint> for DbInterner<'db> { @@ -219,7 +224,9 @@ impl<'db> IrPrint> for DbInterner<'db> { } fn print_debug(t: &ty::FnSig, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + let tys = t.inputs_and_output.as_slice(); + let (output, inputs) = tys.split_last().unwrap(); + write!(fmt, "fn({:?}) -> {:?}", inputs, output) } } @@ -235,6 +242,10 @@ impl<'db> IrPrint>> for DbInterner<'d t: &rustc_type_ir::PatternKind>, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + match t { + ty::PatternKind::Range { start, end } => write!(fmt, "{:?}..={:?}", start, end), + ty::PatternKind::Or(list) => write!(fmt, "or({:?})", list), + ty::PatternKind::NotNull => fmt.write_str("!null"), + } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs index 2bd675ba124e4..29a933f922630 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs @@ -217,6 +217,7 @@ const TARGET_FEATURE_IMPLICATIONS_RAW: &[(&str, &[&str])] = &[ ("relaxed-simd", &["simd128"]), // BPF ("alu32", &[]), + ("allows-misaligned-mem-access", &[]), // CSKY ("10e60", &["7e10"]), ("2e3", &["e2"]), diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 050777a4806da..7f672a697c41e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -51,20 +51,6 @@ macro_rules! diagnostics { )* }; } -// FIXME Accept something like the following in the macro call instead -// diagnostics![ -// pub struct BreakOutsideOfLoop { -// pub expr: InFile>, -// pub is_break: bool, -// pub bad_value_break: bool, -// }, ... -// or more concisely -// BreakOutsideOfLoop { -// expr: InFile>, -// is_break: bool, -// bad_value_break: bool, -// }, ... -// ] diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs index 99ee50fa5848f..da1322de4b641 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs @@ -84,6 +84,7 @@ fn get_replacement_node(ctx: &AssistContext<'_>) -> Option<(ParentType, ast::Exp match parent { ast::LetStmt(it) => it.initializer()?, ast::LetExpr(it) => it.expr()?, + ast::BinExpr(it) => it.rhs()?, ast::Static(it) => it.body()?, ast::Const(it) => it.body()?, _ => return None, @@ -174,6 +175,70 @@ fn foo() { n + 100 }; } +"#, + ); + + check_assist( + add_braces, + r#" +fn foo() { + let x; + x =$0 n + 100; +} +"#, + r#" +fn foo() { + let x; + x = { + n + 100 + }; +} +"#, + ); + + check_assist( + add_braces, + r#" +fn foo() { + if let x =$0 n + 100 {} +} +"#, + r#" +fn foo() { + if let x = { + n + 100 + } {} +} +"#, + ); + } + + #[test] + fn suggest_add_braces_for_const_initializer() { + check_assist( + add_braces, + r#" +const X: i32 =$0 1 + 2; +"#, + r#" +const X: i32 = { + 1 + 2 +}; +"#, + ); + } + + #[test] + fn suggest_add_braces_for_static_initializer() { + check_assist( + add_braces, + r#" +static X: i32 $0= 1 + 2; +"#, + r#" +static X: i32 = { + 1 + 2 +}; "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs index b84ad24cfcef3..6a408e5254fd6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -3,11 +3,7 @@ use ide_db::{ }; use syntax::{ SyntaxToken, T, - ast::{ - self, AstNode, HasLoopBody, - make::{self, tokens}, - syntax_factory::SyntaxFactory, - }, + ast::{self, AstNode, HasLoopBody, syntax_factory::SyntaxFactory}, syntax_editor::{Position, SyntaxEditor}, }; @@ -35,9 +31,9 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let loop_kw = ctx.find_token_syntax_at_offset(T![loop])?; - let loop_expr = loop_kw.parent().and_then(ast::LoopExpr::cast)?; - if loop_expr.label().is_some() { + let loop_expr = ctx.find_node_at_offset::()?; + let loop_kw = loop_token(&loop_expr)?; + if loop_expr.label().is_some() || !loop_kw.text_range().contains_inclusive(ctx.offset()) { return None; } @@ -52,8 +48,8 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let label = make.lifetime("'l"); let elements = vec![ label.syntax().clone().into(), - make::token(T![:]).into(), - tokens::single_space().into(), + make.token(T![:]).into(), + make.whitespace(" ").into(), ]; editor.insert_all(Position::before(&loop_kw), elements); @@ -80,6 +76,14 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> O ) } +fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option { + loop_expr + .syntax() + .children_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| matches!(it.kind(), T![for] | T![loop] | T![while])) +} + fn insert_label_after_token( editor: &mut SyntaxEditor, make: &SyntaxFactory, @@ -88,7 +92,7 @@ fn insert_label_after_token( builder: &mut SourceChangeBuilder, ) { let label = make.lifetime("'l"); - let elements = vec![tokens::single_space().into(), label.syntax().clone().into()]; + let elements = vec![make.whitespace(" ").into(), label.syntax().clone().into()]; editor.insert_all(Position::after(token), elements); if let Some(cap) = ctx.config.snippet_cap { @@ -123,6 +127,48 @@ fn main() { ); } + #[test] + fn add_label_to_while_expr() { + check_assist( + add_label_to_loop, + r#" +fn main() { + while$0 true { + break; + continue; + } +}"#, + r#" +fn main() { + ${1:'l}: while true { + break ${2:'l}; + continue ${0:'l}; + } +}"#, + ); + } + + #[test] + fn add_label_to_for_expr() { + check_assist( + add_label_to_loop, + r#" +fn main() { + for$0 _ in 0..5 { + break; + continue; + } +}"#, + r#" +fn main() { + ${1:'l}: for _ in 0..5 { + break ${2:'l}; + continue ${0:'l}; + } +}"#, + ); + } + #[test] fn add_label_to_outer_loop() { check_assist( @@ -191,6 +237,31 @@ fn main() { break 'l; continue 'l; } +}"#, + ); + } + + #[test] + fn do_not_add_label_if_outside_keyword() { + check_assist_not_applicable( + add_label_to_loop, + r#" +fn main() { + 'l: loop {$0 + break 'l; + continue 'l; + } +}"#, + ); + + check_assist_not_applicable( + add_label_to_loop, + r#" +fn main() { + 'l: while true {$0 + break 'l; + continue 'l; + } }"#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs index 27dbdcf2c4d57..265ee3d2d4e71 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs @@ -1,4 +1,7 @@ -use syntax::ast::{self, AstNode, HasGenericParams, HasName}; +use syntax::{ + SyntaxKind, SyntaxNode, SyntaxToken, + ast::{self, AstNode, HasGenericParams, HasName}, +}; use crate::{AssistContext, AssistId, Assists}; @@ -21,7 +24,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let ref_type_focused = ctx.find_node_at_offset::()?; - if ref_type_focused.lifetime().is_some() { + if ref_type_focused.lifetime().is_some_and(|lifetime| lifetime.text() != "'_") { return None; } @@ -34,10 +37,10 @@ pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_>) - return None; } - let ref_types = fetch_borrowed_types(&node)?; + let changes = fetch_borrowed_types(&node)?; let target = node.syntax().text_range(); - acc.add(AssistId::generate("add_lifetime_to_type"), "Add lifetime", target, |builder| { + acc.add(AssistId::quick_fix("add_lifetime_to_type"), "Add lifetime", target, |builder| { match node.generic_param_list() { Some(gen_param) => { if let Some(left_angle) = gen_param.l_angle_token() { @@ -51,16 +54,21 @@ pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_>) - } } - for ref_type in ref_types { - if let Some(amp_token) = ref_type.amp_token() { - builder.insert(amp_token.text_range().end(), "'a "); + for change in changes { + match change { + Change::Replace(it) => { + builder.replace(it.text_range(), "'a"); + } + Change::Insert(it) => { + builder.insert(it.text_range().end(), "'a "); + } } } }) } -fn fetch_borrowed_types(node: &ast::Adt) -> Option> { - let ref_types: Vec = match node { +fn fetch_borrowed_types(node: &ast::Adt) -> Option> { + let ref_types: Vec<_> = match node { ast::Adt::Enum(enum_) => { let variant_list = enum_.variant_list()?; variant_list @@ -79,55 +87,50 @@ fn fetch_borrowed_types(node: &ast::Adt) -> Option> { } ast::Adt::Union(un) => { let record_field_list = un.record_field_list()?; - record_field_list - .fields() - .filter_map(|r_field| { - if let ast::Type::RefType(ref_type) = r_field.ty()? - && ref_type.lifetime().is_none() - { - return Some(ref_type); - } - - None - }) - .collect() + find_ref_types_from_field_list(&record_field_list.into())? } }; if ref_types.is_empty() { None } else { Some(ref_types) } } -fn find_ref_types_from_field_list(field_list: &ast::FieldList) -> Option> { - let ref_types: Vec = match field_list { - ast::FieldList::RecordFieldList(record_list) => record_list - .fields() - .filter_map(|f| { - if let ast::Type::RefType(ref_type) = f.ty()? - && ref_type.lifetime().is_none() - { - return Some(ref_type); - } - - None - }) - .collect(), - ast::FieldList::TupleFieldList(tuple_field_list) => tuple_field_list - .fields() - .filter_map(|f| { - if let ast::Type::RefType(ref_type) = f.ty()? - && ref_type.lifetime().is_none() - { - return Some(ref_type); - } - - None - }) - .collect(), +fn find_ref_types_from_field_list(field_list: &ast::FieldList) -> Option> { + let ref_types: Vec<_> = match field_list { + ast::FieldList::RecordFieldList(record_list) => { + record_list.fields().flat_map(|f| infer_lifetimes(f.syntax())).collect() + } + ast::FieldList::TupleFieldList(tuple_field_list) => { + tuple_field_list.fields().flat_map(|f| infer_lifetimes(f.syntax())).collect() + } }; if ref_types.is_empty() { None } else { Some(ref_types) } } +enum Change { + Replace(SyntaxToken), + Insert(SyntaxToken), +} + +fn infer_lifetimes(node: &SyntaxNode) -> Vec { + node.children() + .filter(|it| !matches!(it.kind(), SyntaxKind::FN_PTR_TYPE | SyntaxKind::TYPE_BOUND_LIST)) + .flat_map(|it| { + infer_lifetimes(&it) + .into_iter() + .chain(ast::Lifetime::cast(it.clone()).and_then(|lt| { + lt.lifetime_ident_token().filter(|lt| lt.text() == "'_").map(Change::Replace) + })) + .chain( + ast::RefType::cast(it) + .filter(|ty| ty.lifetime().is_none()) + .and_then(|ty| ty.amp_token()) + .map(Change::Insert), + ) + }) + .collect() +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -164,6 +167,24 @@ mod tests { check_assist_not_applicable(add_lifetime_to_type, r#"struct Foo { a: &'a$0 i32 }"#); } + #[test] + fn add_lifetime_to_nested_types() { + check_assist( + add_lifetime_to_type, + r#"struct Foo { a: &$0i32, b: &(&i32, fn(&str) -> &str) }"#, + r#"struct Foo<'a> { a: &'a i32, b: &'a (&'a i32, fn(&str) -> &str) }"#, + ); + } + + #[test] + fn add_lifetime_to_explicit_infer_lifetime() { + check_assist( + add_lifetime_to_type, + r#"struct Foo { a: &'_ $0i32, b: &'_ (&'_ i32, fn(&str) -> &str) }"#, + r#"struct Foo<'a> { a: &'a i32, b: &'a (&'a i32, fn(&str) -> &str) }"#, + ); + } + #[test] fn add_lifetime_to_enum() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index c0ce057d7798d..8124fecff6296 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -245,14 +245,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .arms() .filter(|arm| { if matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))) { - let is_empty_expr = arm.expr().is_none_or(|e| match e { - ast::Expr::BlockExpr(b) => { - b.statements().next().is_none() && b.tail_expr().is_none() - } - ast::Expr::TupleExpr(t) => t.fields().next().is_none(), - _ => false, - }); - if is_empty_expr { + if arm.expr().is_none_or(is_empty_expr) { false } else { cov_mark::hit!(add_missing_match_arms_empty_expr); @@ -347,12 +340,24 @@ fn cursor_at_trivial_match_arm_list( // $0 // } if let Some(last_arm) = match_arm_list.arms().last() { - let last_arm_range = ctx.sema.original_range_opt(last_arm.syntax())?.range; + let last_node = match last_arm.expr() { + Some(expr) => expr.syntax().clone(), + None => last_arm.syntax().clone(), + }; + let last_node_range = ctx.sema.original_range_opt(&last_node)?.range; let match_expr_range = ctx.sema.original_range_opt(match_expr.syntax())?.range; - if last_arm_range.end() <= ctx.offset() && ctx.offset() < match_expr_range.end() { + if last_node_range.end() <= ctx.offset() && ctx.offset() < match_expr_range.end() { cov_mark::hit!(add_missing_match_arms_end_of_last_arm); return Some(()); } + + if ast::Expr::cast(last_node.clone()).is_some_and(is_empty_expr) + && last_node_range.contains(ctx.offset()) + && !last_node.text().contains_char('\n') + { + cov_mark::hit!(add_missing_match_arms_end_of_last_empty_arm); + return Some(()); + } } // match { _$0 => {...} } @@ -371,6 +376,14 @@ fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool { !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var)) } +fn is_empty_expr(e: ast::Expr) -> bool { + match e { + ast::Expr::BlockExpr(b) => b.statements().next().is_none() && b.tail_expr().is_none(), + ast::Expr::TupleExpr(t) => t.fields().next().is_none(), + _ => false, + } +} + // Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check? fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { match (pat, var) { @@ -1066,7 +1079,7 @@ fn main() { #[test] fn add_missing_match_arms_end_of_last_arm() { - cov_mark::check!(add_missing_match_arms_end_of_last_arm); + cov_mark::check_count!(add_missing_match_arms_end_of_last_arm, 2); check_assist( add_missing_match_arms, r#" @@ -1095,6 +1108,103 @@ fn main() { (A::Two, B::Two) => ${3:todo!()},$0 } } +"#, + ); + + check_assist( + add_missing_match_arms, + r#" +enum A { One, Two } +enum B { One, Two } + +fn main() { + let a = A::One; + let b = B::One; + match (a, b) { + (A::Two, B::One) => 2$0, + } +} +"#, + r#" +enum A { One, Two } +enum B { One, Two } + +fn main() { + let a = A::One; + let b = B::One; + match (a, b) { + (A::Two, B::One) => 2, + (A::One, B::One) => ${1:todo!()}, + (A::One, B::Two) => ${2:todo!()}, + (A::Two, B::Two) => ${3:todo!()},$0 + } +} +"#, + ); + } + + #[test] + fn add_missing_match_arms_end_of_last_empty_arm() { + cov_mark::check_count!(add_missing_match_arms_end_of_last_empty_arm, 2); + check_assist( + add_missing_match_arms, + r#" +enum A { One, Two } +enum B { One, Two } + +fn main() { + let a = A::One; + let b = B::One; + match (a, b) { + (A::Two, B::One) => {$0} + } +} +"#, + r#" +enum A { One, Two } +enum B { One, Two } + +fn main() { + let a = A::One; + let b = B::One; + match (a, b) { + (A::Two, B::One) => {} + (A::One, B::One) => ${1:todo!()}, + (A::One, B::Two) => ${2:todo!()}, + (A::Two, B::Two) => ${3:todo!()},$0 + } +} +"#, + ); + + check_assist( + add_missing_match_arms, + r#" +enum A { One, Two } +enum B { One, Two } + +fn main() { + let a = A::One; + let b = B::One; + match (a, b) { + (A::Two, B::One) => ($0) + } +} +"#, + r#" +enum A { One, Two } +enum B { One, Two } + +fn main() { + let a = A::One; + let b = B::One; + match (a, b) { + (A::Two, B::One) => (), + (A::One, B::One) => ${1:todo!()}, + (A::One, B::Two) => ${2:todo!()}, + (A::Two, B::Two) => ${3:todo!()},$0 + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 80d0a6da12434..4ee49702489d2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -197,7 +197,7 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?; let ast::Expr::ClosureExpr(closure_expr) = arg_expr else { return None }; - let closure_body = closure_expr.body()?.clone_for_update(); + let closure_body = closure_expr.body()?; let op_range = method_call.syntax().text_range(); let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index 934779810054f..e88778a62e193 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -533,7 +533,7 @@ fn make_bool_enum(make_pub: bool, make: &SyntaxFactory) -> ast::Enum { ], ), )); - make.enum_( + make.item_enum( [derive_eq], if make_pub { Some(make.visibility_pub()) } else { None }, make.name("Bool"), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 5a223e11301cd..9f9ced98d73b2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -220,7 +220,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) } let body = if wrap_body_in_block { - make::block_expr([], Some(body)) + make::block_expr([], Some(body.reset_indent().indent(1.into()))) } else { ast::BlockExpr::cast(body.syntax().clone()).unwrap() }; @@ -969,6 +969,32 @@ fn foo() { } closure(); } +"#, + ); + check_assist( + convert_closure_to_fn, + r#" +//- minicore: copy +fn foo() { + { + let closure = |$0| match () { + () => {}, + }; + closure(); + } +} +"#, + r#" +fn foo() { + { + fn closure() { + match () { + () => {}, + } + } + closure(); + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs index d64e9ceda2ef2..d409374c16f6d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs @@ -2,7 +2,7 @@ use hir::{Name, sym}; use ide_db::{famous_defs::FamousDefs, syntax_helpers::suggest_name}; use syntax::{ AstNode, - ast::{self, HasAttrs, HasLoopBody, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, + ast::{self, HasAttrs, HasLoopBody, edit::IndentLevel, syntax_factory::SyntaxFactory}, syntax_editor::Position, }; @@ -57,13 +57,13 @@ pub(crate) fn convert_for_loop_to_while_let( { (expr, Some(make.name_ref(method.as_str()))) } else if let ast::Expr::RefExpr(_) = iterable { - (make::expr_paren(iterable).into(), Some(make.name_ref("into_iter"))) + (make.expr_paren(iterable).into(), Some(make.name_ref("into_iter"))) } else { (iterable, Some(make.name_ref("into_iter"))) }; let iterable = if let Some(method) = method { - make::expr_method_call(iterable, method, make::arg_list([])).into() + make.expr_method_call(iterable, method, make.arg_list([])).into() } else { iterable }; @@ -89,17 +89,18 @@ pub(crate) fn convert_for_loop_to_while_let( for_loop.syntax(), &mut editor, for_loop.attrs().map(|it| it.clone_for_update()), + &make, ); editor.insert( Position::before(for_loop.syntax()), - make::tokens::whitespace(format!("\n{indent}").as_str()), + make.whitespace(format!("\n{indent}").as_str()), ); editor.insert(Position::before(for_loop.syntax()), mut_expr.syntax()); - let opt_pat = make.tuple_struct_pat(make::ext::ident_path("Some"), [pat]); + let opt_pat = make.tuple_struct_pat(make.ident_path("Some"), [pat]); let iter_next_expr = make.expr_method_call( - make.expr_path(make::ext::ident_path(&tmp_var)), + make.expr_path(make.ident_path(&tmp_var)), make.name_ref("next"), make.arg_list([]), ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index 6a74d214512fb..66ccd2a4e4093 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -1,6 +1,6 @@ use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait}; use syntax::ast::edit::IndentLevel; -use syntax::ast::{self, AstNode, HasGenericArgs, HasName, make}; +use syntax::ast::{self, AstNode, HasGenericArgs, HasName, syntax_factory::SyntaxFactory}; use syntax::syntax_editor::{Element, Position}; use crate::{AssistContext, AssistId, Assists}; @@ -74,36 +74,25 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> "Convert From to TryFrom", impl_.syntax().text_range(), |builder| { + let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(impl_.syntax()); - editor.replace( - trait_ty.syntax(), - make::ty(&format!("TryFrom<{from_type}>")).syntax().clone_for_update(), - ); + + editor.replace(trait_ty.syntax(), make.ty(&format!("TryFrom<{from_type}>")).syntax()); editor.replace( from_fn_return_type.syntax(), - make::ty("Result").syntax().clone_for_update(), - ); - editor - .replace(from_fn_name.syntax(), make::name("try_from").syntax().clone_for_update()); - editor.replace( - tail_expr.syntax(), - wrap_ok(tail_expr.clone()).syntax().clone_for_update(), + make.ty("Result").syntax(), ); + editor.replace(from_fn_name.syntax(), make.name("try_from").syntax()); + editor.replace(tail_expr.syntax(), wrap_ok(&make, tail_expr.clone()).syntax()); for r in return_exprs { - let t = r.expr().unwrap_or_else(make::ext::expr_unit); - editor.replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update()); + let t = r.expr().unwrap_or_else(|| make.expr_unit()); + editor.replace(t.syntax(), wrap_ok(&make, t.clone()).syntax()); } - let error_type = ast::AssocItem::TypeAlias(make::ty_alias( - None, - "Error", - None, - None, - None, - Some((make::ty_unit(), None)), - )) - .clone_for_update(); + let error_type_alias = + make.ty_alias(None, "Error", None, None, None, Some((make.ty("()"), None))); + let error_type = ast::AssocItem::TypeAlias(error_type_alias); if let Some(cap) = ctx.config.snippet_cap && let ast::AssocItem::TypeAlias(type_alias) = &error_type @@ -117,22 +106,19 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> editor.insert_all( Position::after(associated_l_curly), vec![ - make::tokens::whitespace(&format!("\n{indent}")).syntax_element(), + make.whitespace(&format!("\n{indent}")).syntax_element(), error_type.syntax().syntax_element(), - make::tokens::whitespace("\n").syntax_element(), + make.whitespace("\n").syntax_element(), ], ); + editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } -fn wrap_ok(expr: ast::Expr) -> ast::Expr { - make::expr_call( - make::expr_path(make::ext::ident_path("Ok")), - make::arg_list(std::iter::once(expr)), - ) - .into() +fn wrap_ok(make: &SyntaxFactory, expr: ast::Expr) -> ast::Expr { + make.expr_call(make.expr_path(make.path_from_text("Ok")), make.arg_list([expr])).into() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index e518c39dabc27..42fceb85336b9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -1,14 +1,18 @@ use either::Either; use ide_db::{defs::Definition, search::FileReference}; -use itertools::Itertools; use syntax::{ - SyntaxKind, - ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility}, + NodeOrToken, SyntaxKind, SyntaxNode, T, + algo::next_non_trivia_token, + ast::{ + self, AstNode, HasAttrs, HasGenericParams, HasVisibility, syntax_factory::SyntaxFactory, + }, match_ast, - syntax_editor::{Position, SyntaxEditor}, + syntax_editor::{Element, Position, SyntaxEditor}, }; -use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder}; +use crate::{ + AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder, utils::cover_edit_range, +}; // Assist: convert_named_struct_to_tuple_struct // @@ -81,17 +85,17 @@ pub(crate) fn convert_named_struct_to_tuple_struct( AssistId::refactor_rewrite("convert_named_struct_to_tuple_struct"), "Convert to tuple struct", strukt_or_variant.syntax().text_range(), - |edit| { - edit_field_references(ctx, edit, record_fields.fields()); - edit_struct_references(ctx, edit, strukt_def); - edit_struct_def(ctx, edit, &strukt_or_variant, record_fields); + |builder| { + edit_field_references(ctx, builder, record_fields.fields()); + edit_struct_references(ctx, builder, strukt_def); + edit_struct_def(ctx, builder, &strukt_or_variant, record_fields); }, ) } fn edit_struct_def( ctx: &AssistContext<'_>, - edit: &mut SourceChangeBuilder, + builder: &mut SourceChangeBuilder, strukt: &Either, record_fields: ast::RecordFieldList, ) { @@ -108,24 +112,23 @@ fn edit_struct_def( let field = ast::TupleField::cast(field_syntax)?; Some(field) }); - let tuple_fields = ast::make::tuple_field_list(tuple_fields); - let record_fields_text_range = record_fields.syntax().text_range(); - edit.edit_file(ctx.vfs_file_id()); - edit.replace(record_fields_text_range, tuple_fields.syntax().text()); + let make = SyntaxFactory::without_mappings(); + let mut edit = builder.make_editor(strukt.syntax()); + + let tuple_fields = make.tuple_field_list(tuple_fields); + let mut elements = vec![tuple_fields.syntax().clone().into()]; if let Either::Left(strukt) = strukt { if let Some(w) = strukt.where_clause() { - let mut where_clause = w.to_string(); - if where_clause.ends_with(',') { - where_clause.pop(); - } - where_clause.push(';'); + edit.delete(w.syntax()); - edit.delete(w.syntax().text_range()); - edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text()); - edit.insert(record_fields_text_range.end(), where_clause); - edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text()); + elements.extend([ + make.whitespace("\n").into(), + remove_trailing_comma(w).into(), + make.token(T![;]).into(), + make.whitespace("\n").into(), + ]); if let Some(tok) = strukt .generic_param_list() @@ -133,25 +136,28 @@ fn edit_struct_def( .and_then(|tok| tok.next_token()) .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE) { - edit.delete(tok.text_range()); + edit.delete(tok); } } else { - edit.insert(record_fields_text_range.end(), ";"); + elements.push(make.token(T![;]).into()); } } + edit.replace_with_many(record_fields.syntax(), elements); if let Some(tok) = record_fields .l_curly_token() .and_then(|tok| tok.prev_token()) .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE) { - edit.delete(tok.text_range()) + edit.delete(tok) } + + builder.add_file_edits(ctx.vfs_file_id(), edit); } fn edit_struct_references( ctx: &AssistContext<'_>, - edit: &mut SourceChangeBuilder, + builder: &mut SourceChangeBuilder, strukt: Either, ) { let strukt_def = match strukt { @@ -161,17 +167,20 @@ fn edit_struct_references( let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); for (file_id, refs) in usages { - edit.edit_file(file_id.file_id(ctx.db())); + let source = ctx.sema.parse(file_id); + let mut edit = builder.make_editor(source.syntax()); for r in refs { - process_struct_name_reference(ctx, r, edit); + process_struct_name_reference(ctx, r, &mut edit, &source); } + builder.add_file_edits(file_id.file_id(ctx.db()), edit); } } fn process_struct_name_reference( ctx: &AssistContext<'_>, r: FileReference, - edit: &mut SourceChangeBuilder, + edit: &mut SyntaxEditor, + source: &ast::SourceFile, ) -> Option<()> { // First check if it's the last semgnet of a path that directly belongs to a record // expression/pattern. @@ -192,36 +201,26 @@ fn process_struct_name_reference( match_ast! { match parent { ast::RecordPat(record_struct_pat) => { - // When we failed to get the original range for the whole struct expression node, + // When we failed to get the original range for the whole struct pattern node, // we can't provide any reasonable edit. Leave it untouched. - let file_range = ctx.sema.original_range_opt(record_struct_pat.syntax())?; - edit.replace( - file_range.range, - ast::make::tuple_struct_pat( - record_struct_pat.path()?, - record_struct_pat - .record_pat_field_list()? - .fields() - .filter_map(|pat| pat.pat()) - .chain(record_struct_pat.record_pat_field_list()? - .rest_pat() - .map(Into::into)) - ) - .to_string() + record_to_tuple_struct_like( + ctx, + source, + edit, + record_struct_pat.record_pat_field_list()?, + |it| it.fields().filter_map(|it| it.name_ref()), ); }, ast::RecordExpr(record_expr) => { - // When we failed to get the original range for the whole struct pattern node, + // When we failed to get the original range for the whole struct expression node, // we can't provide any reasonable edit. Leave it untouched. - let file_range = ctx.sema.original_range_opt(record_expr.syntax())?; - let path = record_expr.path()?; - let args = record_expr - .record_expr_field_list()? - .fields() - .filter_map(|f| f.expr()) - .join(", "); - - edit.replace(file_range.range, format!("{path}({args})")); + record_to_tuple_struct_like( + ctx, + source, + edit, + record_expr.record_expr_field_list()?, + |it| it.fields().filter_map(|it| it.name_ref()), + ); }, _ => {} } @@ -230,11 +229,67 @@ fn process_struct_name_reference( Some(()) } +fn record_to_tuple_struct_like( + ctx: &AssistContext<'_>, + source: &ast::SourceFile, + edit: &mut SyntaxEditor, + field_list: T, + fields: impl FnOnce(&T) -> I, +) -> Option<()> +where + T: AstNode, + I: IntoIterator, +{ + let make = SyntaxFactory::without_mappings(); + let orig = ctx.sema.original_range_opt(field_list.syntax())?; + let list_range = cover_edit_range(source, orig.range); + + let l_curly = match list_range.start() { + NodeOrToken::Node(node) => node.first_token()?, + NodeOrToken::Token(t) => t.clone(), + }; + let r_curly = match list_range.end() { + NodeOrToken::Node(node) => node.last_token()?, + NodeOrToken::Token(t) => t.clone(), + }; + + if l_curly.kind() == T!['{'] { + delete_whitespace(edit, l_curly.prev_token()); + delete_whitespace(edit, l_curly.next_token()); + edit.replace(l_curly, make.token(T!['('])); + } + if r_curly.kind() == T!['}'] { + delete_whitespace(edit, r_curly.prev_token()); + edit.replace(r_curly, make.token(T![')'])); + } + + for name_ref in fields(&field_list) { + let Some(orig) = ctx.sema.original_range_opt(name_ref.syntax()) else { continue }; + let name_range = cover_edit_range(source, orig.range); + + if let Some(colon) = next_non_trivia_token(name_range.end().clone()) + && colon.kind() == T![:] + { + edit.delete(&colon); + edit.delete_all(name_range); + + if let Some(next) = next_non_trivia_token(colon.clone()) + && next.kind() != T!['}'] + { + // Avoid overlapping delete whitespace on `{ field: }` + delete_whitespace(edit, colon.next_token()); + } + } + } + Some(()) +} + fn edit_field_references( ctx: &AssistContext<'_>, - edit: &mut SourceChangeBuilder, + builder: &mut SourceChangeBuilder, fields: impl Iterator, ) { + let make = SyntaxFactory::without_mappings(); for (index, field) in fields.enumerate() { let field = match ctx.sema.to_def(&field) { Some(it) => it, @@ -243,19 +298,46 @@ fn edit_field_references( let def = Definition::Field(field); let usages = def.usages(&ctx.sema).all(); for (file_id, refs) in usages { - edit.edit_file(file_id.file_id(ctx.db())); + let source = ctx.sema.parse(file_id); + let mut edit = builder.make_editor(source.syntax()); + for r in refs { if let Some(name_ref) = r.name.as_name_ref() { // Only edit the field reference if it's part of a `.field` access if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() { - edit.replace(r.range, index.to_string()); + edit.replace_all( + cover_edit_range(&source, r.range), + vec![make.name_ref(&index.to_string()).syntax().clone().into()], + ); } } } + + builder.add_file_edits(file_id.file_id(ctx.db()), edit); } } } +fn delete_whitespace(edit: &mut SyntaxEditor, whitespace: Option) { + let Some(whitespace) = whitespace else { return }; + let NodeOrToken::Token(token) = whitespace.syntax_element() else { return }; + + if token.kind() == SyntaxKind::WHITESPACE && !token.text().contains('\n') { + edit.delete(token); + } +} + +fn remove_trailing_comma(w: ast::WhereClause) -> SyntaxNode { + let w = w.syntax().clone_subtree(); + let mut editor = SyntaxEditor::new(w.clone()); + if let Some(last) = w.last_child_or_token() + && last.kind() == T![,] + { + editor.delete(last); + } + editor.finish().new_root().clone() +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -677,6 +759,102 @@ where ); } + #[test] + fn convert_constructor_expr_uses_self() { + // regression test for #21595 + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct $0Foo { field1: u32 } +impl Foo { + fn clone(&self) -> Self { + Self { field1: self.field1 } + } +}"#, + r#" +struct Foo(u32); +impl Foo { + fn clone(&self) -> Self { + Self(self.0) + } +}"#, + ); + + check_assist( + convert_named_struct_to_tuple_struct, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} +struct $0Foo { field1: u32 } +impl Foo { + fn clone(&self) -> Self { + id!(Self { field1: self.field1 }) + } +}"#, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} +struct Foo(u32); +impl Foo { + fn clone(&self) -> Self { + id!(Self(self.0)) + } +}"#, + ); + } + + #[test] + fn convert_pat_uses_self() { + // regression test for #21595 + check_assist( + convert_named_struct_to_tuple_struct, + r#" +enum Foo { + $0Value { field: &'static Foo }, + Nil, +} +fn foo(foo: &Foo) { + if let Foo::Value { field: Foo::Value { field } } = foo {} +}"#, + r#" +enum Foo { + Value(&'static Foo), + Nil, +} +fn foo(foo: &Foo) { + if let Foo::Value(Foo::Value(field)) = foo {} +}"#, + ); + + check_assist( + convert_named_struct_to_tuple_struct, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} +enum Foo { + $0Value { field: &'static Foo }, + Nil, +} +fn foo(foo: &Foo) { + if let id!(Foo::Value { field: Foo::Value { field } }) = foo {} +}"#, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} +enum Foo { + Value(&'static Foo), + Nil, +} +fn foo(foo: &Foo) { + if let id!(Foo::Value(Foo::Value(field))) = foo {} +}"#, + ); + } + #[test] fn not_applicable_other_than_record_variant() { check_assist_not_applicable( @@ -1042,7 +1220,9 @@ struct Struct(i32); fn test() { id! { - let s = Struct(42); + let s = Struct( + 42, + ); let Struct(value) = s; let Struct(inner) = s; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index dc51bf4b5b8c1..db45916792206 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -10,14 +10,14 @@ use syntax::{ ast::{ self, edit::{AstNodeEdit, IndentLevel}, - make, + syntax_factory::SyntaxFactory, }, }; use crate::{ AssistId, assist_context::{AssistContext, Assists}, - utils::{invert_boolean_expression_legacy, is_never_block}, + utils::{invert_boolean_expression, is_never_block}, }; // Assist: convert_to_guarded_return @@ -69,6 +69,7 @@ fn if_expr_to_guarded_return( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { + let make = SyntaxFactory::without_mappings(); let else_block = match if_expr.else_branch() { Some(ast::ElseBranch::Block(block_expr)) if is_never_block(&ctx.sema, &block_expr) => { Some(block_expr) @@ -88,7 +89,7 @@ fn if_expr_to_guarded_return( return None; } - let let_chains = flat_let_chain(cond); + let let_chains = flat_let_chain(cond, &make); let then_branch = if_expr.then_branch()?; let then_block = then_branch.stmt_list()?; @@ -110,7 +111,8 @@ fn if_expr_to_guarded_return( let early_expression = else_block .or_else(|| { - early_expression(parent_container, &ctx.sema).map(ast::make::tail_only_block_expr) + early_expression(parent_container, &ctx.sema, &make) + .map(ast::make::tail_only_block_expr) })? .reset_indent(); @@ -133,6 +135,7 @@ fn if_expr_to_guarded_return( "Convert to guarded return", target, |edit| { + let make = SyntaxFactory::without_mappings(); let if_indent_level = IndentLevel::from_node(if_expr.syntax()); let replacement = let_chains.into_iter().map(|expr| { if let ast::Expr::LetExpr(let_expr) = &expr @@ -140,15 +143,15 @@ fn if_expr_to_guarded_return( { // If-let. let let_else_stmt = - make::let_else_stmt(pat, None, expr, early_expression.clone()); + make.let_else_stmt(pat, None, expr, early_expression.clone()); let let_else_stmt = let_else_stmt.indent(if_indent_level); let_else_stmt.syntax().clone() } else { // If. let new_expr = { - let then_branch = clean_stmt_block(&early_expression); - let cond = invert_boolean_expression_legacy(expr); - make::expr_if(cond, then_branch, None).indent(if_indent_level) + let then_branch = clean_stmt_block(&early_expression, &make); + let cond = invert_boolean_expression(&make, expr); + make.expr_if(cond, then_branch, None).indent(if_indent_level) }; new_expr.syntax().clone() } @@ -159,7 +162,7 @@ fn if_expr_to_guarded_return( .enumerate() .flat_map(|(i, node)| { (i != 0) - .then(|| make::tokens::whitespace(newline).into()) + .then(|| make.whitespace(newline).into()) .into_iter() .chain(node.children_with_tokens()) }) @@ -201,12 +204,13 @@ fn let_stmt_to_guarded_return( let happy_pattern = try_enum.happy_pattern(pat); let target = let_stmt.syntax().text_range(); + let make = SyntaxFactory::without_mappings(); let early_expression: ast::Expr = { let parent_block = let_stmt.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; let parent_container = parent_block.syntax().parent()?; - early_expression(parent_container, &ctx.sema)? + early_expression(parent_container, &ctx.sema, &make)? }; acc.add( @@ -215,9 +219,10 @@ fn let_stmt_to_guarded_return( target, |edit| { let let_indent_level = IndentLevel::from_node(let_stmt.syntax()); + let make = SyntaxFactory::without_mappings(); let replacement = { - let let_else_stmt = make::let_else_stmt( + let let_else_stmt = make.let_else_stmt( happy_pattern, let_stmt.ty(), expr.reset_indent(), @@ -228,6 +233,7 @@ fn let_stmt_to_guarded_return( }; let mut editor = edit.make_editor(let_stmt.syntax()); editor.replace(let_stmt.syntax(), replacement); + editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -236,38 +242,39 @@ fn let_stmt_to_guarded_return( fn early_expression( parent_container: SyntaxNode, sema: &Semantics<'_, RootDatabase>, + make: &SyntaxFactory, ) -> Option { let return_none_expr = || { - let none_expr = make::expr_path(make::ext::ident_path("None")); - make::expr_return(Some(none_expr)) + let none_expr = make.expr_path(make.ident_path("None")); + make.expr_return(Some(none_expr)) }; if let Some(fn_) = ast::Fn::cast(parent_container.clone()) && let Some(fn_def) = sema.to_def(&fn_) && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &fn_def.ret_type(sema.db)) { - return Some(return_none_expr()); + return Some(return_none_expr().into()); } if let Some(body) = ast::ClosureExpr::cast(parent_container.clone()).and_then(|it| it.body()) && let Some(ret_ty) = sema.type_of_expr(&body).map(TypeInfo::original) && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &ret_ty) { - return Some(return_none_expr()); + return Some(return_none_expr().into()); } Some(match parent_container.kind() { - WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make::expr_continue(None), - FN | CLOSURE_EXPR => make::expr_return(None), + WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make.expr_continue(None).into(), + FN | CLOSURE_EXPR => make.expr_return(None).into(), _ => return None, }) } -fn flat_let_chain(mut expr: ast::Expr) -> Vec { +fn flat_let_chain(mut expr: ast::Expr, make: &SyntaxFactory) -> Vec { let mut chains = vec![]; let mut reduce_cond = |rhs| { if !matches!(rhs, ast::Expr::LetExpr(_)) && let Some(last) = chains.pop_if(|last| !matches!(last, ast::Expr::LetExpr(_))) { - chains.push(make::expr_bin_op(rhs, ast::BinaryOp::LogicOp(ast::LogicOp::And), last)); + chains.push(make.expr_bin_op(rhs, ast::BinaryOp::LogicOp(ast::LogicOp::And), last)); } else { chains.push(rhs); } @@ -286,12 +293,12 @@ fn flat_let_chain(mut expr: ast::Expr) -> Vec { chains } -fn clean_stmt_block(block: &ast::BlockExpr) -> ast::BlockExpr { +fn clean_stmt_block(block: &ast::BlockExpr, make: &SyntaxFactory) -> ast::BlockExpr { if block.statements().next().is_none() && let Some(tail_expr) = block.tail_expr() && block.modifier().is_none() { - make::block_expr(once(make::expr_stmt(tail_expr).into()), None) + make.block_expr(once(make.expr_stmt(tail_expr).into()), None) } else { block.clone() } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index f8b9bb68db817..f1eae83866371 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -1,17 +1,21 @@ use either::Either; -use hir::FileRangeWrapper; -use ide_db::defs::{Definition, NameRefClass}; -use std::ops::RangeInclusive; +use ide_db::{ + defs::{Definition, NameRefClass}, + search::FileReference, +}; use syntax::{ - SyntaxElement, SyntaxKind, SyntaxNode, T, TextSize, + SyntaxKind, T, ast::{ - self, AstNode, HasAttrs, HasGenericParams, HasVisibility, syntax_factory::SyntaxFactory, + self, AstNode, HasArgList, HasAttrs, HasGenericParams, HasVisibility, + syntax_factory::SyntaxFactory, }, match_ast, syntax_editor::{Element, Position, SyntaxEditor}, }; -use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder}; +use crate::{ + AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder, utils::cover_edit_range, +}; // Assist: convert_tuple_struct_to_named_struct // @@ -147,93 +151,121 @@ fn edit_struct_references( }; let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); - let edit_node = |node: SyntaxNode| -> Option { - let make = SyntaxFactory::without_mappings(); - match_ast! { - match node { - ast::TupleStructPat(tuple_struct_pat) => { - Some(make.record_pat_with_fields( - tuple_struct_pat.path()?, - generate_record_pat_list(&tuple_struct_pat, names), - ).syntax().clone()) - }, - // for tuple struct creations like Foo(42) - ast::CallExpr(call_expr) => { - let path = call_expr.syntax().descendants().find_map(ast::PathExpr::cast).and_then(|expr| expr.path())?; - - // this also includes method calls like Foo::new(42), we should skip them - if let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) { - match NameRefClass::classify(&ctx.sema, &name_ref) { - Some(NameRefClass::Definition(Definition::SelfType(_), _)) => {}, - Some(NameRefClass::Definition(def, _)) if def == strukt_def => {}, - _ => return None, - }; - } + for (file_id, refs) in usages { + let source = ctx.sema.parse(file_id); + let mut editor = edit.make_editor(source.syntax()); - let arg_list = call_expr.syntax().descendants().find_map(ast::ArgList::cast)?; - Some( - make.record_expr( - path, - ast::make::record_expr_field_list(arg_list.args().zip(names).map( - |(expr, name)| { - ast::make::record_expr_field( - ast::make::name_ref(&name.to_string()), - Some(expr), - ) - }, - )), - ).syntax().clone() - ) - }, - _ => None, - } + for r in refs { + process_struct_name_reference(ctx, r, &mut editor, &source, &strukt_def, names); } - }; - for (file_id, refs) in usages { - let source = ctx.sema.parse(file_id); - let source = source.syntax(); - - let mut editor = edit.make_editor(source); - for r in refs.iter().rev() { - if let Some((old_node, new_node)) = r - .name - .syntax() - .ancestors() - .find_map(|node| Some((node.clone(), edit_node(node.clone())?))) - { - if let Some(old_node) = ctx.sema.original_syntax_node_rooted(&old_node) { - editor.replace(old_node, new_node); - } else { - let FileRangeWrapper { file_id: _, range } = ctx.sema.original_range(&old_node); - let parent = source.covering_element(range); - match parent { - SyntaxElement::Token(token) => { - editor.replace(token, new_node.syntax_element()); - } - SyntaxElement::Node(parent_node) => { - // replace the part of macro - // ``` - // foo!(a, Test::A(0)); - // ^^^^^^^^^^^^^^^ // parent_node - // ^^^^^^^^^^ // replace_range - // ``` - let start = parent_node - .children_with_tokens() - .find(|t| t.text_range().contains(range.start())); - let end = parent_node - .children_with_tokens() - .find(|t| t.text_range().contains(range.end() - TextSize::new(1))); - if let (Some(start), Some(end)) = (start, end) { - let replace_range = RangeInclusive::new(start, end); - editor.replace_all(replace_range, vec![new_node.into()]); - } - } + edit.add_file_edits(file_id.file_id(ctx.db()), editor); + } +} + +fn process_struct_name_reference( + ctx: &AssistContext<'_>, + r: FileReference, + editor: &mut SyntaxEditor, + source: &ast::SourceFile, + strukt_def: &Definition, + names: &[ast::Name], +) -> Option<()> { + let make = SyntaxFactory::without_mappings(); + let name_ref = r.name.as_name_ref()?; + let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?; + let full_path = path_segment.syntax().parent().and_then(ast::Path::cast)?.top_path(); + + if full_path.segment()?.name_ref()? != *name_ref { + // `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the + // struct we want to edit. + return None; + } + + let parent = full_path.syntax().parent()?; + match_ast! { + match parent { + ast::TupleStructPat(tuple_struct_pat) => { + let range = ctx.sema.original_range_opt(tuple_struct_pat.syntax())?.range; + let new = make.record_pat_with_fields( + full_path, + generate_record_pat_list(&tuple_struct_pat, names), + ); + editor.replace_all(cover_edit_range(source, range), vec![new.syntax().clone().into()]); + }, + ast::PathExpr(path_expr) => { + let call_expr = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; + + // this also includes method calls like Foo::new(42), we should skip them + match NameRefClass::classify(&ctx.sema, name_ref) { + Some(NameRefClass::Definition(Definition::SelfType(_), _)) => {}, + Some(NameRefClass::Definition(def, _)) if def == *strukt_def => {}, + _ => return None, + } + + let arg_list = call_expr.arg_list()?; + let mut first_insert = vec![]; + for (expr, name) in arg_list.args().zip(names) { + let range = ctx.sema.original_range_opt(expr.syntax())?.range; + let place = cover_edit_range(source, range); + let elements = vec![ + make.name_ref(&name.text()).syntax().clone().into(), + make.token(T![:]).into(), + make.whitespace(" ").into(), + ]; + if first_insert.is_empty() { + // XXX: SyntaxEditor cannot insert after deleted element + first_insert = elements; + } else { + editor.insert_all(Position::before(place.start()), elements); } } - } + process_delimiter(ctx, source, editor, &arg_list, first_insert); + }, + _ => {} } - edit.add_file_edits(file_id.file_id(ctx.db()), editor); + } + Some(()) +} + +fn process_delimiter( + ctx: &AssistContext<'_>, + source: &ast::SourceFile, + editor: &mut SyntaxEditor, + list: &impl AstNode, + first_insert: Vec, +) { + let Some(range) = ctx.sema.original_range_opt(list.syntax()) else { return }; + let place = cover_edit_range(source, range.range); + + let l_paren = match place.start() { + syntax::NodeOrToken::Node(node) => node.first_token(), + syntax::NodeOrToken::Token(t) => Some(t.clone()), + }; + let r_paren = match place.end() { + syntax::NodeOrToken::Node(node) => node.last_token(), + syntax::NodeOrToken::Token(t) => Some(t.clone()), + }; + + let make = SyntaxFactory::without_mappings(); + if let Some(l_paren) = l_paren + && l_paren.kind() == T!['('] + { + let mut open_delim = vec![ + make.whitespace(" ").into(), + make.token(T!['{']).into(), + make.whitespace(" ").into(), + ]; + open_delim.extend(first_insert); + editor.replace_with_many(l_paren, open_delim); + } + if let Some(r_paren) = r_paren + && r_paren.kind() == T![')'] + { + editor.replace_with_many( + r_paren, + vec![make.whitespace(" ").into(), make.token(T!['}']).into()], + ); } } @@ -252,13 +284,15 @@ fn edit_field_references( let usages = def.usages(&ctx.sema).all(); for (file_id, refs) in usages { let source = ctx.sema.parse(file_id); - let source = source.syntax(); - let mut editor = edit.make_editor(source); + let mut editor = edit.make_editor(source.syntax()); for r in refs { if let Some(name_ref) = r.name.as_name_ref() - && let Some(original) = ctx.sema.original_ast_node(name_ref.clone()) + && let Some(original) = ctx.sema.original_range_opt(name_ref.syntax()) { - editor.replace(original.syntax(), name.syntax()); + editor.replace_all( + cover_edit_range(&source, original.range), + vec![name.syntax().clone().into()], + ); } } edit.add_file_edits(file_id.file_id(ctx.db()), editor); @@ -739,6 +773,64 @@ where "#, ); } + + #[test] + fn convert_expr_uses_self() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} +struct T$0(u8); +fn test(t: T) { + T(t.0); + id!(T(t.0)); +}"#, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} +struct T { field1: u8 } +fn test(t: T) { + T { field1: t.field1 }; + id!(T { field1: t.field1 }); +}"#, + ); + } + + #[test] + #[ignore = "FIXME overlap edits in nested uses self"] + fn convert_pat_uses_self() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} +enum T { + $0Value(&'static T), + Nil, +} +fn test(t: T) { + if let T::Value(T::Value(t)) = t {} + if let id!(T::Value(T::Value(t))) = t {} +}"#, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} +enum T { + Value { field1: &'static T }, + Nil, +} +fn test(t: T) { + if let T::Value { field1: T::Value { field1: t } } = t {} + if let id!(T::Value { field1: T::Value { field1: t } }) = t {} +}"#, + ); + } + #[test] fn not_applicable_other_than_tuple_variant() { check_assist_not_applicable( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs index 9fd8b4b3159ea..f8215d6723d3f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs @@ -6,7 +6,7 @@ use syntax::{ ast::{ self, HasLoopBody, edit::{AstNodeEdit, IndentLevel}, - make, + syntax_factory::SyntaxFactory, }, syntax_editor::{Element, Position}, }; @@ -14,7 +14,7 @@ use syntax::{ use crate::{ AssistId, assist_context::{AssistContext, Assists}, - utils::invert_boolean_expression_legacy, + utils::invert_boolean_expression, }; // Assist: convert_while_to_loop @@ -52,44 +52,47 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) "Convert while to loop", target, |builder| { + let make = SyntaxFactory::without_mappings(); let mut edit = builder.make_editor(while_expr.syntax()); let while_indent_level = IndentLevel::from_node(while_expr.syntax()); - let break_block = make::block_expr( - iter::once(make::expr_stmt(make::expr_break(None, None)).into()), - None, - ) - .indent(IndentLevel(1)); + let break_block = make + .block_expr( + iter::once(make.expr_stmt(make.expr_break(None, None).into()).into()), + None, + ) + .indent(IndentLevel(1)); edit.replace_all( while_kw.syntax_element()..=while_cond.syntax().syntax_element(), - vec![make::token(T![loop]).syntax_element()], + vec![make.token(T![loop]).syntax_element()], ); if is_pattern_cond(while_cond.clone()) { let then_branch = while_body.reset_indent().indent(IndentLevel(1)); - let if_expr = make::expr_if(while_cond, then_branch, Some(break_block.into())); - let stmts = iter::once(make::expr_stmt(if_expr.into()).into()); - let block_expr = make::block_expr(stmts, None); + let if_expr = make.expr_if(while_cond, then_branch, Some(break_block.into())); + let stmts = iter::once(make.expr_stmt(if_expr.into()).into()); + let block_expr = make.block_expr(stmts, None); edit.replace(while_body.syntax(), block_expr.indent(while_indent_level).syntax()); } else { - let if_cond = invert_boolean_expression_legacy(while_cond); - let if_expr = make::expr_if(if_cond, break_block, None).indent(while_indent_level); + let if_cond = invert_boolean_expression(&make, while_cond); + let if_expr = make.expr_if(if_cond, break_block, None).indent(while_indent_level); if !while_body.syntax().text().contains_char('\n') { edit.insert( Position::after(&l_curly), - make::tokens::whitespace(&format!("\n{while_indent_level}")), + make.whitespace(&format!("\n{while_indent_level}")), ); } edit.insert_all( Position::after(&l_curly), vec![ - make::tokens::whitespace(&format!("\n{}", while_indent_level + 1)).into(), + make.whitespace(&format!("\n{}", while_indent_level + 1)).into(), if_expr.syntax().syntax_element(), ], ); }; + edit.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), edit); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 4c4cee1d7811f..0f5ef0548cf5b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -381,23 +381,20 @@ fn build_usage_edit( Some(field_expr) => Some({ let field_name: SmolStr = field_expr.name_ref()?.to_string().into(); let new_field_name = field_names.get(&field_name)?; - let new_expr = ast::make::expr_path(ast::make::ext::ident_path(new_field_name)); + let new_expr = make.expr_path(make.ident_path(new_field_name)); // If struct binding is a reference, we might need to deref field usages if data.is_ref { let (replace_expr, ref_data) = determine_ref_and_parens(ctx, &field_expr); - ( - replace_expr.syntax().clone_for_update(), - ref_data.wrap_expr(new_expr).syntax().clone_for_update(), - ) + (replace_expr.syntax().clone(), ref_data.wrap_expr(new_expr, make).syntax().clone()) } else { - (field_expr.syntax().clone(), new_expr.syntax().clone_for_update()) + (field_expr.syntax().clone(), new_expr.syntax().clone()) } }), None => Some(( usage.name.syntax().as_node().unwrap().clone(), make.expr_macro( - ast::make::ext::ident_path("todo"), + make.ident_path("todo"), make.token_tree(syntax::SyntaxKind::L_PAREN, []), ) .syntax() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs index 9976e34e730cc..4022030159324 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs @@ -9,7 +9,6 @@ use syntax::{ ast::{ self, edit::{AstNodeEdit, IndentLevel}, - make, syntax_factory::SyntaxFactory, }, }; @@ -68,41 +67,46 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op AssistId::refactor_rewrite("desugar_try_expr_match"), "Replace try expression with match", target, - |edit| { + |builder| { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(try_expr.syntax()); + let sad_pat = match try_enum { - TryEnum::Option => make::path_pat(make::ext::ident_path("None")), - TryEnum::Result => make::tuple_struct_pat( - make::ext::ident_path("Err"), - iter::once(make::path_pat(make::ext::ident_path("err"))), - ) - .into(), + TryEnum::Option => make.path_pat(make.ident_path("None")), + TryEnum::Result => make + .tuple_struct_pat( + make.ident_path("Err"), + iter::once(make.path_pat(make.ident_path("err"))), + ) + .into(), }; let sad_expr = match try_enum { - TryEnum::Option => { - make::expr_return(Some(make::expr_path(make::ext::ident_path("None")))) - } - TryEnum::Result => make::expr_return(Some( - make::expr_call( - make::expr_path(make::ext::ident_path("Err")), - make::arg_list(iter::once(make::expr_path(make::ext::ident_path("err")))), + TryEnum::Option => make.expr_return(Some(make.expr_path(make.ident_path("None")))), + TryEnum::Result => make.expr_return(Some( + make.expr_call( + make.expr_path(make.ident_path("Err")), + make.arg_list(iter::once(make.expr_path(make.ident_path("err")))), ) .into(), )), }; - let happy_arm = make::match_arm( - try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()), + let happy_arm = make.match_arm( + try_enum.happy_pattern(make.ident_pat(false, false, make.name("it")).into()), None, - make::expr_path(make::ext::ident_path("it")), + make.expr_path(make.ident_path("it")), ); - let sad_arm = make::match_arm(sad_pat, None, sad_expr); + let sad_arm = make.match_arm(sad_pat, None, sad_expr.into()); - let match_arm_list = make::match_arm_list([happy_arm, sad_arm]); + let match_arm_list = make.match_arm_list([happy_arm, sad_arm]); - let expr_match = make::expr_match(expr.clone(), match_arm_list) + let expr_match = make + .expr_match(expr.clone(), match_arm_list) .indent(IndentLevel::from_node(try_expr.syntax())); - edit.replace_ast::(try_expr.clone().into(), expr_match.into()); + editor.replace(try_expr.syntax(), expr_match.syntax()); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index 61af2de6ec683..35e8baa18aca7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -8,7 +8,7 @@ use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::WHITESPACE, SyntaxToken, T, - ast::{self, TokenTree, make, syntax_factory::SyntaxFactory}, + ast::{self, TokenTree, syntax_factory::SyntaxFactory}, }; // Assist: extract_expressions_from_format_string @@ -57,6 +57,7 @@ pub(crate) fn extract_expressions_from_format_string( "Extract format expressions", tt.syntax().text_range(), |edit| { + let make = SyntaxFactory::without_mappings(); // Extract existing arguments in macro let mut raw_tokens = tt.token_trees_and_tokens().skip(1).collect_vec(); let format_string_index = format_str_index(&raw_tokens, &fmt_string); @@ -94,14 +95,14 @@ pub(crate) fn extract_expressions_from_format_string( let mut new_tt_bits = raw_tokens; let mut placeholder_indexes = vec![]; - new_tt_bits.push(NodeOrToken::Token(make::tokens::literal(&new_fmt))); + new_tt_bits.push(NodeOrToken::Token(make.expr_literal(&new_fmt).token().clone())); for arg in extracted_args { if matches!(arg, Arg::Expr(_) | Arg::Placeholder) { // insert ", " before each arg new_tt_bits.extend_from_slice(&[ - NodeOrToken::Token(make::token(T![,])), - NodeOrToken::Token(make::tokens::single_space()), + NodeOrToken::Token(make.token(T![,])), + NodeOrToken::Token(make.whitespace(" ")), ]); } @@ -109,7 +110,7 @@ pub(crate) fn extract_expressions_from_format_string( Arg::Expr(s) => { // insert arg let expr = ast::Expr::parse(&s, ctx.edition()).syntax_node(); - let mut expr_tt = utils::tt_from_syntax(expr); + let mut expr_tt = utils::tt_from_syntax(expr, &make); new_tt_bits.append(&mut expr_tt); } Arg::Placeholder => { @@ -120,7 +121,7 @@ pub(crate) fn extract_expressions_from_format_string( } None => { placeholder_indexes.push(new_tt_bits.len()); - new_tt_bits.push(NodeOrToken::Token(make::token(T![_]))); + new_tt_bits.push(NodeOrToken::Token(make.token(T![_]))); } } } @@ -129,7 +130,6 @@ pub(crate) fn extract_expressions_from_format_string( } // Insert new args - let make = SyntaxFactory::with_mappings(); let new_tt = make.token_tree(tt_delimiter, new_tt_bits); let mut editor = edit.make_editor(tt.syntax()); editor.replace(tt.syntax(), new_tt.syntax()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 7c60184142176..7071106970a13 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -9,7 +9,6 @@ use syntax::{ ast::{ self, AstNode, edit::{AstNodeEdit, IndentLevel}, - make, syntax_factory::SyntaxFactory, }, syntax_editor::Position, @@ -75,7 +74,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op .next() .and_then(ast::Expr::cast) { - expr.syntax().ancestors().find_map(valid_target_expr)?.syntax().clone() + expr.syntax().ancestors().find_map(valid_target_expr(ctx))?.syntax().clone() } else { return None; } @@ -96,7 +95,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let to_extract = node .descendants() .take_while(|it| range.contains_range(it.text_range())) - .find_map(valid_target_expr)?; + .find_map(valid_target_expr(ctx))?; let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted); if matches!(&ty, Some(ty_info) if ty_info.is_unit()) { @@ -176,7 +175,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let mut editor = edit.make_editor(&expr_replace); let pat_name = make.name(&var_name); - let name_expr = make.expr_path(make::ext::ident_path(&var_name)); + let name_expr = make.expr_path(make.ident_path(&var_name)); if let Some(cap) = ctx.config.snippet_cap { let tabstop = edit.make_tabstop_before(cap); @@ -233,7 +232,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op Position::before(place), vec![ new_stmt.syntax().clone().into(), - make::tokens::whitespace(&trailing_ws).into(), + make.whitespace(&trailing_ws).into(), ], ); @@ -283,14 +282,19 @@ fn peel_parens(mut expr: ast::Expr) -> ast::Expr { /// Check whether the node is a valid expression which can be extracted to a variable. /// In general that's true for any expression, but in some cases that would produce invalid code. -fn valid_target_expr(node: SyntaxNode) -> Option { - match node.kind() { - SyntaxKind::PATH_EXPR | SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None, +fn valid_target_expr(ctx: &AssistContext<'_>) -> impl Fn(SyntaxNode) -> Option { + |node| match node.kind() { + SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None, SyntaxKind::BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()), SyntaxKind::RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()), SyntaxKind::BLOCK_EXPR => { ast::BlockExpr::cast(node).filter(|it| it.is_standalone()).map(ast::Expr::from) } + SyntaxKind::PATH_EXPR => { + let path_expr = ast::PathExpr::cast(node)?; + let path_resolution = ctx.sema.resolve_path(&path_expr.path()?)?; + like_const_value(ctx, path_resolution).then_some(path_expr.into()) + } _ => ast::Expr::cast(node), } } @@ -455,6 +459,31 @@ impl Anchor { } } +fn like_const_value(ctx: &AssistContext<'_>, path_resolution: hir::PathResolution) -> bool { + let db = ctx.db(); + let adt_like_const_value = |adt: Option| matches!(adt, Some(hir::Adt::Struct(s)) if s.kind(db) == hir::StructKind::Unit); + match path_resolution { + hir::PathResolution::Def(def) => match def { + hir::ModuleDef::Adt(adt) => adt_like_const_value(Some(adt)), + hir::ModuleDef::Variant(variant) => variant.kind(db) == hir::StructKind::Unit, + hir::ModuleDef::TypeAlias(ty) => adt_like_const_value(ty.ty(db).as_adt()), + hir::ModuleDef::Const(_) | hir::ModuleDef::Static(_) => true, + hir::ModuleDef::Trait(_) + | hir::ModuleDef::BuiltinType(_) + | hir::ModuleDef::Macro(_) + | hir::ModuleDef::Module(_) => false, + hir::ModuleDef::Function(_) => false, // no extract named function + }, + hir::PathResolution::SelfType(ty) => adt_like_const_value(ty.self_ty(db).as_adt()), + hir::PathResolution::ConstParam(_) => true, + hir::PathResolution::Local(_) + | hir::PathResolution::TypeParam(_) + | hir::PathResolution::BuiltinAttr(_) + | hir::PathResolution::ToolModule(_) + | hir::PathResolution::DeriveHelper(_) => false, + } +} + #[cfg(test)] mod tests { // NOTE: We use check_assist_by_label, but not check_assist_not_applicable_by_label @@ -1747,6 +1776,27 @@ fn main() { ); } + #[test] + fn extract_non_local_path_expr() { + check_assist_by_label( + extract_variable, + r#" +struct Foo; +fn foo() -> Foo { + $0Foo$0 +} +"#, + r#" +struct Foo; +fn foo() -> Foo { + let $0foo = Foo; + foo +} +"#, + "Extract into variable", + ); + } + #[test] fn extract_var_for_return_not_applicable() { check_assist_not_applicable(extract_variable, "fn foo() { $0return$0; } "); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index c1eb1a74ecdeb..63033c7d5e398 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -4,7 +4,7 @@ use syntax::{ ast::{ self, AstNode, HasGenericParams, HasName, HasVisibility as _, edit::{AstNodeEdit, IndentLevel}, - make, + syntax_factory::SyntaxFactory, }, syntax_editor::Position, }; @@ -100,7 +100,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { continue; }; - let field = make::ext::field_from_idents(["self", &field_name])?; acc.add_group( &GroupLabel("Generate delegate methods…".to_owned()), @@ -108,10 +107,14 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' format!("Generate delegate for `{field_name}.{name}()`",), target, |edit| { + let make = SyntaxFactory::without_mappings(); + let field = make + .field_from_idents(["self", &field_name]) + .expect("always be a valid expression"); // Create the function let method_source = match ctx.sema.source(method) { Some(source) => { - let v = source.value.clone_for_update(); + let v = source.value; let source_scope = ctx.sema.scope(v.syntax()); let target_scope = ctx.sema.scope(strukt.syntax()); if let (Some(s), Some(t)) = (source_scope, target_scope) { @@ -132,42 +135,42 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let is_unsafe = method_source.unsafe_token().is_some(); let is_gen = method_source.gen_token().is_some(); - let fn_name = make::name(&name); + let fn_name = make.name(&name); let type_params = method_source.generic_param_list(); let where_clause = method_source.where_clause(); let params = - method_source.param_list().unwrap_or_else(|| make::param_list(None, [])); + method_source.param_list().unwrap_or_else(|| make.param_list(None, [])); // compute the `body` let arg_list = method_source .param_list() - .map(convert_param_list_to_arg_list) - .unwrap_or_else(|| make::arg_list([])); + .map(|v| convert_param_list_to_arg_list(v, &make)) + .unwrap_or_else(|| make.arg_list([])); - let tail_expr = - make::expr_method_call(field, make::name_ref(&name), arg_list).into(); + let tail_expr = make.expr_method_call(field, make.name_ref(&name), arg_list).into(); let tail_expr_finished = - if is_async { make::expr_await(tail_expr) } else { tail_expr }; - let body = make::block_expr([], Some(tail_expr_finished)); + if is_async { make.expr_await(tail_expr).into() } else { tail_expr }; + let body = make.block_expr([], Some(tail_expr_finished)); let ret_type = method_source.ret_type(); - let f = make::fn_( - None, - vis, - fn_name, - type_params, - where_clause, - params, - body, - ret_type, - is_async, - is_const, - is_unsafe, - is_gen, - ) - .indent(IndentLevel(1)); + let f = make + .fn_( + None, + vis, + fn_name, + type_params, + where_clause, + params, + body, + ret_type, + is_async, + is_const, + is_unsafe, + is_gen, + ) + .indent(IndentLevel(1)); let item = ast::AssocItem::Fn(f.clone()); let mut editor = edit.make_editor(strukt.syntax()); @@ -179,7 +182,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' Some(item) } None => { - let assoc_item_list = make::assoc_item_list(Some(vec![item])); + let assoc_item_list = make.assoc_item_list(vec![item]); editor.insert( Position::last_child_of(impl_def.syntax()), assoc_item_list.syntax(), @@ -192,17 +195,16 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let ty_params = strukt.generic_param_list(); let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); let where_clause = strukt.where_clause(); - let assoc_item_list = make::assoc_item_list(Some(vec![item])); + let assoc_item_list = make.assoc_item_list(vec![item]); - let impl_def = make::impl_( + let impl_def = make.impl_( None, ty_params, ty_args, - make::ty_path(make::ext::ident_path(name)), + syntax::ast::Type::PathType(make.ty_path(make.ident_path(name))), where_clause, Some(assoc_item_list), - ) - .clone_for_update(); + ); // Fixup impl_def indentation let indent = strukt.indent_level(); @@ -213,7 +215,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' editor.insert_all( Position::after(strukt.syntax()), vec![ - make::tokens::whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), impl_def.syntax().clone().into(), ], ); @@ -227,6 +229,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let tabstop = edit.make_tabstop_before(cap); editor.add_annotation(fn_.syntax(), tabstop); } + editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, )?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 921f04f2a56bc..f703e4dc4ab25 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -782,7 +782,7 @@ fn func_assoc_item( }; // Build argument list with self expression prepended - let other_args = convert_param_list_to_arg_list(l); + let other_args = convert_param_list_to_arg_list(l, &make); let all_args: Vec = std::iter::once(tail_expr_self).chain(other_args.args()).collect(); let args = make.arg_list(all_args); @@ -790,13 +790,13 @@ fn func_assoc_item( make.expr_call(make.expr_path(qualified_path), args).into() } None => make - .expr_call(make.expr_path(qualified_path), convert_param_list_to_arg_list(l)) + .expr_call(make.expr_path(qualified_path), convert_param_list_to_arg_list(l, &make)) .into(), }, None => make .expr_call( make.expr_path(qualified_path), - convert_param_list_to_arg_list(make.param_list(None, Vec::new())), + convert_param_list_to_arg_list(make.param_list(None, Vec::new()), &make), ) .into(), }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 8bc4d50cf6af2..1286abe3565e2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -1,8 +1,10 @@ use crate::assist_context::{AssistContext, Assists}; use ide_db::assists::AssistId; use syntax::{ - AstNode, SyntaxKind, T, - ast::{self, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, make}, + AstNode, AstToken, SyntaxKind, T, + ast::{ + self, HasDocComments, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, make, + }, syntax_editor::{Position, SyntaxEditor}, }; @@ -45,7 +47,7 @@ use syntax::{ // }; // } // -// trait ${0:NewTrait} { +// trait ${0:Create} { // // Used as an associated constant. // const CONST_ASSOC: usize = N * 4; // @@ -54,7 +56,7 @@ use syntax::{ // const_maker! {i32, 7} // } // -// impl ${0:NewTrait} for Foo { +// impl ${0:Create} for Foo { // // Used as an associated constant. // const CONST_ASSOC: usize = N * 4; // @@ -107,7 +109,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ }; let trait_ast = make::trait_( false, - "NewTrait", + &trait_name(&impl_assoc_items).text(), impl_ast.generic_param_list(), impl_ast.where_clause(), trait_items, @@ -133,6 +135,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ let mut editor = builder.make_editor(impl_ast.syntax()); impl_assoc_items.assoc_items().for_each(|item| { remove_items_visibility(&mut editor, &item); + remove_doc_comments(&mut editor, &item); }); editor.insert_all(Position::before(impl_name.syntax()), elements); @@ -160,6 +163,18 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ Some(()) } +fn trait_name(items: &ast::AssocItemList) -> ast::Name { + let mut fn_names = items + .assoc_items() + .filter_map(|x| if let ast::AssocItem::Fn(f) = x { f.name() } else { None }); + fn_names + .next() + .and_then(|name| { + fn_names.next().is_none().then(|| make::name(&stdx::to_camel_case(&name.text()))) + }) + .unwrap_or_else(|| make::name("NewTrait")) +} + /// `E0449` Trait items always share the visibility of their trait fn remove_items_visibility(editor: &mut SyntaxEditor, item: &ast::AssocItem) { if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) { @@ -175,6 +190,17 @@ fn remove_items_visibility(editor: &mut SyntaxEditor, item: &ast::AssocItem) { } } +fn remove_doc_comments(editor: &mut SyntaxEditor, item: &ast::AssocItem) { + for doc in item.doc_comments() { + if let Some(next) = doc.syntax().next_token() + && next.kind() == SyntaxKind::WHITESPACE + { + editor.delete(next); + } + editor.delete(doc.syntax()); + } +} + fn strip_body(editor: &mut SyntaxEditor, item: &ast::AssocItem) { if let ast::AssocItem::Fn(f) = item && let Some(body) = f.body() @@ -226,11 +252,47 @@ impl F$0oo { r#" struct Foo(f64); -trait NewTrait { +trait Add { fn add(&mut self, x: f64); } -impl NewTrait for Foo { +impl Add for Foo { + fn add(&mut self, x: f64) { + self.0 += x; + } +}"#, + ) + } + + #[test] + fn test_remove_doc_comments() { + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +struct Foo(f64); + +impl F$0oo { + /// Add `x` + /// + /// # Examples + #[cfg(true)] + fn add(&mut self, x: f64) { + self.0 += x; + } +}"#, + r#" +struct Foo(f64); + +trait Add { + /// Add `x` + /// + /// # Examples + #[cfg(true)] + fn add(&mut self, x: f64); +} + +impl Add for Foo { + #[cfg(true)] fn add(&mut self, x: f64) { self.0 += x; } @@ -339,11 +401,11 @@ impl F$0oo { r#" struct Foo; -trait NewTrait { +trait AFunc { fn a_func() -> Option<()>; } -impl NewTrait for Foo { +impl AFunc for Foo { fn a_func() -> Option<()> { Some(()) } @@ -373,17 +435,39 @@ mod a { }"#, r#" mod a { - trait NewTrait { + trait Foo { fn foo(); } - impl NewTrait for S { + impl Foo for S { fn foo() {} } }"#, ) } + #[test] + fn test_multi_fn_impl_not_suggest_trait_name() { + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +impl S$0 { + fn foo() {} + fn bar() {} +}"#, + r#" +trait NewTrait { + fn foo(); + fn bar(); +} + +impl NewTrait for S { + fn foo() {} + fn bar() {} +}"#, + ) + } + #[test] fn test_snippet_cap_is_some() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index 5d4bdc6ec76cd..f55ef4229e587 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -1,3 +1,4 @@ +use either::{Either, for_both}; use hir::{PathResolution, Semantics}; use ide_db::{ EditionedFileId, RootDatabase, @@ -5,8 +6,9 @@ use ide_db::{ search::{FileReference, FileReferenceNode, UsageSearchResult}, }; use syntax::{ - SyntaxElement, TextRange, + Direction, TextRange, ast::{self, AstNode, AstToken, HasName, syntax_factory::SyntaxFactory}, + syntax_editor::{Element, SyntaxEditor}, }; use crate::{ @@ -36,12 +38,15 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) let InlineData { let_stmt, delete_let, references, target } = if let Some(path_expr) = ctx.find_node_at_offset::() { inline_usage(&ctx.sema, path_expr, range, file_id) - } else if let Some(let_stmt) = ctx.find_node_at_offset::() { + } else if let Some(let_stmt) = ctx.find_node_at_offset() { inline_let(&ctx.sema, let_stmt, range, file_id) } else { None }?; - let initializer_expr = let_stmt.initializer()?; + let initializer_expr = match &let_stmt { + either::Either::Left(it) => it.initializer()?, + either::Either::Right(it) => it.expr()?, + }; let wrap_in_parens = references .into_iter() @@ -81,13 +86,15 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) let mut editor = builder.make_editor(&target); if delete_let { editor.delete(let_stmt.syntax()); - if let Some(whitespace) = let_stmt - .syntax() - .next_sibling_or_token() - .and_then(SyntaxElement::into_token) - .and_then(ast::Whitespace::cast) + + if let Some(bin_expr) = let_stmt.syntax().parent().and_then(ast::BinExpr::cast) + && let Some(op_token) = bin_expr.op_token() { - editor.delete(whitespace.syntax()); + editor.delete(&op_token); + remove_whitespace(op_token, Direction::Prev, &mut editor); + remove_whitespace(let_stmt.syntax(), Direction::Prev, &mut editor); + } else { + remove_whitespace(let_stmt.syntax(), Direction::Next, &mut editor); } } @@ -116,7 +123,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) } struct InlineData { - let_stmt: ast::LetStmt, + let_stmt: Either, delete_let: bool, target: ast::NameOrNameRef, references: Vec, @@ -124,11 +131,11 @@ struct InlineData { fn inline_let( sema: &Semantics<'_, RootDatabase>, - let_stmt: ast::LetStmt, + let_stmt: Either, range: TextRange, file_id: EditionedFileId, ) -> Option { - let bind_pat = match let_stmt.pat()? { + let bind_pat = match for_both!(&let_stmt, it => it.pat())? { ast::Pat::IdentPat(pat) => pat, _ => return None, }; @@ -187,7 +194,7 @@ fn inline_usage( let bind_pat = source.as_ident_pat()?; - let let_stmt = ast::LetStmt::cast(bind_pat.syntax().parent()?)?; + let let_stmt = AstNode::cast(bind_pat.syntax().parent()?)?; let UsageSearchResult { mut references } = Definition::Local(local).usages(sema).all(); let mut references = references.remove(&file_id)?; @@ -197,6 +204,23 @@ fn inline_usage( Some(InlineData { let_stmt, delete_let, target: ast::NameOrNameRef::NameRef(name), references }) } +fn remove_whitespace(elem: impl Element, dir: Direction, editor: &mut SyntaxEditor) { + let token = match elem.syntax_element() { + syntax::NodeOrToken::Node(node) => match dir { + Direction::Next => node.last_token(), + Direction::Prev => node.first_token(), + }, + syntax::NodeOrToken::Token(t) => Some(t), + }; + let next_token = match dir { + Direction::Next => token.and_then(|it| it.next_token()), + Direction::Prev => token.and_then(|it| it.prev_token()), + }; + if let Some(whitespace) = next_token.and_then(ast::Whitespace::cast) { + editor.delete(whitespace.syntax()); + } +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -404,6 +428,38 @@ fn foo() { ); } + #[test] + fn test_inline_let_expr() { + check_assist( + inline_local_variable, + r" +fn bar(a: usize) {} +fn foo() { + if let a$0 = 1 + && true + { + a + 1; + if a > 10 {} + while a > 10 {} + let b = a * 10; + bar(a); + } +}", + r" +fn bar(a: usize) {} +fn foo() { + if true + { + 1 + 1; + if 1 > 10 {} + while 1 > 10 {} + let b = 1 * 10; + bar(1); + } +}", + ); + } + #[test] fn test_not_inline_mut_variable() { cov_mark::check!(test_not_inline_mut_variable); @@ -816,6 +872,70 @@ fn f() { ); } + #[test] + fn let_expr_works_on_local_usage() { + check_assist( + inline_local_variable, + r#" +fn f() { + if let xyz = 0 + && true + { + xyz$0; + } +} +"#, + r#" +fn f() { + if true + { + 0; + } +} +"#, + ); + + check_assist( + inline_local_variable, + r#" +fn f() { + if let xyz = true + && xyz$0 + { + } +} +"#, + r#" +fn f() { + if true + { + } +} +"#, + ); + + check_assist( + inline_local_variable, + r#" +fn f() { + if true + && let xyz = 0 + { + xyz$0; + } +} +"#, + r#" +fn f() { + if true + { + 0; + } +} +"#, + ); + } + #[test] fn does_not_remove_let_when_multiple_usages() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index c7a48f3261a9f..f3ebe61078192 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -12,7 +12,7 @@ use itertools::Itertools; use syntax::ast::syntax_factory::SyntaxFactory; use syntax::syntax_editor::SyntaxEditor; use syntax::{ - AstNode, NodeOrToken, SyntaxNode, + AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast::{self, HasGenericParams, HasName}, }; @@ -322,12 +322,42 @@ fn create_replacement( if let Some(old_lifetime) = ast::Lifetime::cast(syntax.clone()) { if let Some(new_lifetime) = lifetime_map.0.get(&old_lifetime.to_string()) { if new_lifetime.text() == "'_" { + // Check if this lifetime is inside a LifetimeArg (in angle brackets) + if let Some(lifetime_arg) = + old_lifetime.syntax().parent().and_then(ast::LifetimeArg::cast) + { + // Remove LifetimeArg and associated comma/whitespace + let lifetime_arg_syntax = lifetime_arg.syntax(); + removals.push(NodeOrToken::Node(lifetime_arg_syntax.clone())); + + // Remove comma and whitespace (look forward then backward) + let comma_and_ws: Vec<_> = lifetime_arg_syntax + .siblings_with_tokens(syntax::Direction::Next) + .skip(1) + .take_while(|it| it.as_token().is_some()) + .take_while_inclusive(|it| it.kind() == T![,]) + .collect(); + + if comma_and_ws.iter().any(|it| it.kind() == T![,]) { + removals.extend(comma_and_ws); + } else { + // No comma after, try before + let comma_and_ws: Vec<_> = lifetime_arg_syntax + .siblings_with_tokens(syntax::Direction::Prev) + .skip(1) + .take_while(|it| it.as_token().is_some()) + .take_while_inclusive(|it| it.kind() == T![,]) + .collect(); + removals.extend(comma_and_ws); + } + continue; + } removals.push(NodeOrToken::Node(syntax.clone())); - - if let Some(ws) = syntax.next_sibling_or_token() { - removals.push(ws.clone()); + if let Some(ws) = syntax.next_sibling_or_token() + && ws.kind() == SyntaxKind::WHITESPACE + { + removals.push(ws); } - continue; } @@ -349,6 +379,34 @@ fn create_replacement( } } + // Deduplicate removals to avoid intersecting changes + removals.sort_by_key(|n| n.text_range().start()); + removals.dedup(); + + // Remove GenericArgList entirely if all its args are being removed (avoids empty angle brackets) + let generic_arg_lists_to_check: Vec<_> = + updated_concrete_type.descendants().filter_map(ast::GenericArgList::cast).collect(); + + for generic_arg_list in generic_arg_lists_to_check { + let will_be_empty = generic_arg_list.generic_args().all(|arg| match arg { + ast::GenericArg::LifetimeArg(lt_arg) => removals.iter().any(|removal| { + if let NodeOrToken::Node(node) = removal { node == lt_arg.syntax() } else { false } + }), + _ => false, + }); + + if will_be_empty && generic_arg_list.generic_args().next().is_some() { + removals.retain(|removal| { + if let NodeOrToken::Node(node) = removal { + !node.ancestors().any(|anc| anc == *generic_arg_list.syntax()) + } else { + true + } + }); + removals.push(NodeOrToken::Node(generic_arg_list.syntax().clone())); + } + } + for (old, new) in replacements { editor.replace(old, new); } @@ -946,6 +1004,48 @@ trait Tr { ); } + #[test] + fn inline_types_with_lifetime() { + check_assist( + inline_type_alias_uses, + r#" +struct A<'a, 'b>(pub &'a mut &'b mut ()); + +type $0T<'a, 'b> = A<'a, 'b>; + +fn foo(_: T) {} +"#, + r#" +struct A<'a, 'b>(pub &'a mut &'b mut ()); + + + +fn foo(_: A) {} +"#, + ); + } + + #[test] + fn mixed_lifetime_and_type_args() { + check_assist( + inline_type_alias, + r#" +type Foo<'a, T> = Bar<'a, T>; +struct Bar<'a, T>(&'a T); +fn main() { + let a: $0Foo; +} +"#, + r#" +type Foo<'a, T> = Bar<'a, T>; +struct Bar<'a, T>(&'a T); +fn main() { + let a: Bar; +} +"#, + ); + } + mod inline_type_alias_uses { use crate::{handlers::inline_type_alias::inline_type_alias_uses, tests::check_assist}; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs index bf82d8df9b58f..c8cb7bb60f69c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs @@ -1,13 +1,13 @@ use ide_db::syntax_helpers::node_ext::is_pattern_cond; use syntax::{ T, - ast::{self, AstNode}, + ast::{self, AstNode, syntax_factory::SyntaxFactory}, }; use crate::{ AssistId, assist_context::{AssistContext, Assists}, - utils::invert_boolean_expression_legacy, + utils::invert_boolean_expression, }; // Assist: invert_if @@ -50,7 +50,8 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() }; acc.add(AssistId::refactor_rewrite("invert_if"), "Invert if", if_range, |edit| { - let flip_cond = invert_boolean_expression_legacy(cond.clone()); + let make = SyntaxFactory::without_mappings(); + let flip_cond = invert_boolean_expression(&make, cond.clone()); edit.replace_ast(cond, flip_cond); let else_node = else_block.syntax(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs index 9ba73d23dd243..42bc05811fd14 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs @@ -49,8 +49,9 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio SyntaxElement::Node(n) => n, SyntaxElement::Token(t) => t.parent()?, }; - let mut selected_nodes = - parent_node.children().filter(|it| selection_range.contains_range(it.text_range())); + let mut selected_nodes = parent_node.children().filter(|it| { + selection_range.intersect(it.text_range()).is_some_and(|it| !it.is_empty()) + }); let first_selected = selected_nodes.next()?; let edits = match_ast! { @@ -677,6 +678,25 @@ use std::fmt::Result; ); } + #[test] + fn merge_partial_selection_uses() { + cov_mark::check!(merge_with_selected_use_item_neighbors); + check_assist( + merge_imports, + r" +use std::fmt::Error; +$0use std::fmt::Display; +use std::fmt::Debug; +use std::fmt::Write; +use$0 std::fmt::Result; +", + r" +use std::fmt::Error; +use std::fmt::{Debug, Display, Result, Write}; +", + ); + } + #[test] fn merge_selection_use_trees() { cov_mark::check!(merge_with_selected_use_tree_neighbors); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index b4c347ff36a7c..80587372e5165 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -3,7 +3,7 @@ use syntax::{ SyntaxKind::WHITESPACE, TextRange, ast::{ - AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make, + AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, prec::ExprPrecedence, syntax_factory::SyntaxFactory, }, syntax_editor::Element, @@ -53,14 +53,15 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) let space_after_arrow = match_arm.fat_arrow_token()?.next_sibling_or_token(); let arm_expr = match_arm.expr()?; + let make = SyntaxFactory::without_mappings(); let if_branch = chain([&match_arm], &rest_arms) .rfold(None, |else_branch, arm| { if let Some(guard) = arm.guard() { - let then_branch = crate::utils::wrap_block(&arm.expr()?); + let then_branch = crate::utils::wrap_block(&arm.expr()?, &make); let guard_condition = guard.condition()?.reset_indent(); - Some(make::expr_if(guard_condition, then_branch, else_branch).into()) + Some(make.expr_if(guard_condition, then_branch, else_branch).into()) } else { - arm.expr().map(|it| crate::utils::wrap_block(&it).into()) + arm.expr().map(|it| crate::utils::wrap_block(&it, &make).into()) } })? .indent(arm_expr.indent_level()); @@ -84,7 +85,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) if let Some(element) = space_after_arrow && element.kind() == WHITESPACE { - edit.replace(element, make::tokens::single_space()); + edit.replace(element, make.whitespace(" ")); } edit.delete(guard.syntax()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index 495a84d62b07d..8b9e6570e917b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -1,6 +1,6 @@ use hir::{AsAssocItem, AssocItem, AssocItemContainer, ItemInNs, ModuleDef, db::HirDatabase}; use ide_db::assists::AssistId; -use syntax::{AstNode, ast}; +use syntax::{AstNode, ast, ast::syntax_factory::SyntaxFactory}; use crate::{ assist_context::{AssistContext, Assists}, @@ -52,19 +52,25 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> cfg, )?; - let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call); + let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call.clone(), resolved_call); acc.add( AssistId::refactor_rewrite("qualify_method_call"), format!("Qualify `{ident}` method call"), range, |builder| { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(call.syntax()); qualify_candidate.qualify( - |replace_with: String| builder.replace(range, replace_with), + |_| {}, + &mut editor, + &make, &receiver_path, item_in_ns, current_edition, - ) + ); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index b3cf2969650a8..c059f758c47e2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -11,7 +11,8 @@ use syntax::Edition; use syntax::ast::HasGenericArgs; use syntax::{ AstNode, ast, - ast::{HasArgList, make}, + ast::{HasArgList, syntax_factory::SyntaxFactory}, + syntax_editor::SyntaxEditor, }; use crate::{ @@ -54,25 +55,25 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let qualify_candidate = match candidate { ImportCandidate::Path(candidate) if !candidate.qualifier.is_empty() => { cov_mark::hit!(qualify_path_qualifier_start); - let path = ast::Path::cast(syntax_under_caret)?; + let path = ast::Path::cast(syntax_under_caret.clone())?; let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) } ImportCandidate::Path(_) => { cov_mark::hit!(qualify_path_unqualified_name); - let path = ast::Path::cast(syntax_under_caret)?; + let path = ast::Path::cast(syntax_under_caret.clone())?; let generics = path.segment()?.generic_arg_list(); QualifyCandidate::UnqualifiedName(generics) } ImportCandidate::TraitAssocItem(_) => { cov_mark::hit!(qualify_path_trait_assoc_item); - let path = ast::Path::cast(syntax_under_caret)?; + let path = ast::Path::cast(syntax_under_caret.clone())?; let (qualifier, segment) = (path.qualifier()?, path.segment()?); QualifyCandidate::TraitAssocItem(qualifier, segment) } ImportCandidate::TraitMethod(_) => { cov_mark::hit!(qualify_path_trait_method); - let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; + let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret.clone())?; QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) } }; @@ -101,12 +102,18 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option label(ctx.db(), candidate, &import, current_edition), range, |builder| { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(&syntax_under_caret); qualify_candidate.qualify( |replace_with: String| builder.replace(range, replace_with), + &mut editor, + &make, &import.import_path, import.item_to_import, current_edition, - ) + ); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); } @@ -124,6 +131,8 @@ impl QualifyCandidate<'_> { pub(crate) fn qualify( &self, mut replacer: impl FnMut(String), + editor: &mut SyntaxEditor, + make: &SyntaxFactory, import: &hir::ModPath, item: hir::ItemInNs, edition: Edition, @@ -142,10 +151,10 @@ impl QualifyCandidate<'_> { replacer(format!("<{qualifier} as {import}>::{segment}")); } QualifyCandidate::TraitMethod(db, mcall_expr) => { - Self::qualify_trait_method(db, mcall_expr, replacer, import, item); + Self::qualify_trait_method(db, mcall_expr, editor, make, import, item); } QualifyCandidate::ImplMethod(db, mcall_expr, hir_fn) => { - Self::qualify_fn_call(db, mcall_expr, replacer, import, hir_fn); + Self::qualify_fn_call(db, mcall_expr, editor, make, import, hir_fn); } } } @@ -153,7 +162,8 @@ impl QualifyCandidate<'_> { fn qualify_fn_call( db: &RootDatabase, mcall_expr: &ast::MethodCallExpr, - mut replacer: impl FnMut(String), + editor: &mut SyntaxEditor, + make: &SyntaxFactory, import: ast::Path, hir_fn: &hir::Function, ) -> Option<()> { @@ -165,15 +175,17 @@ impl QualifyCandidate<'_> { if let Some(self_access) = hir_fn.self_param(db).map(|sp| sp.access(db)) { let receiver = match self_access { - hir::Access::Shared => make::expr_ref(receiver, false), - hir::Access::Exclusive => make::expr_ref(receiver, true), + hir::Access::Shared => make.expr_ref(receiver, false), + hir::Access::Exclusive => make.expr_ref(receiver, true), hir::Access::Owned => receiver, }; let arg_list = match arg_list { - Some(args) => make::arg_list(iter::once(receiver).chain(args)), - None => make::arg_list(iter::once(receiver)), + Some(args) => make.arg_list(iter::once(receiver).chain(args)), + None => make.arg_list(iter::once(receiver)), }; - replacer(format!("{import}::{method_name}{generics}{arg_list}")); + let call_path = make.path_from_text(&format!("{import}::{method_name}{generics}")); + let call_expr = make.expr_call(make.expr_path(call_path), arg_list); + editor.replace(mcall_expr.syntax(), call_expr.syntax()); } Some(()) } @@ -181,14 +193,15 @@ impl QualifyCandidate<'_> { fn qualify_trait_method( db: &RootDatabase, mcall_expr: &ast::MethodCallExpr, - replacer: impl FnMut(String), + editor: &mut SyntaxEditor, + make: &SyntaxFactory, import: ast::Path, item: hir::ItemInNs, ) -> Option<()> { let trait_method_name = mcall_expr.name_ref()?; let trait_ = item_as_trait(db, item)?; let method = find_trait_method(db, trait_, &trait_method_name)?; - Self::qualify_fn_call(db, mcall_expr, replacer, import, &method) + Self::qualify_fn_call(db, mcall_expr, editor, make, import, &method) } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index d22e951b5dab0..38d8c38ef23d6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -1,8 +1,11 @@ use either::Either; use ide_db::syntax_helpers::suggest_name; -use syntax::ast::{self, AstNode, HasArgList, syntax_factory::SyntaxFactory}; +use syntax::ast::{self, AstNode, HasArgList, prec::ExprPrecedence, syntax_factory::SyntaxFactory}; -use crate::{AssistContext, AssistId, Assists, utils::cover_let_chain}; +use crate::{ + AssistContext, AssistId, Assists, + utils::{cover_let_chain, wrap_paren, wrap_paren_in_call}, +}; // Assist: replace_is_some_with_if_let_some // @@ -39,6 +42,7 @@ pub(crate) fn replace_is_method_with_if_let_method( match method_kind { "is_some" | "is_ok" => { let receiver = call_expr.receiver()?; + let make = SyntaxFactory::with_mappings(); let mut name_generator = suggest_name::NameGenerator::new_from_scope_locals( ctx.sema.scope(has_cond.syntax()), @@ -48,7 +52,7 @@ pub(crate) fn replace_is_method_with_if_let_method( } else { name_generator.for_variable(&receiver, &ctx.sema) }; - let (pat, predicate) = method_predicate(&call_expr).unzip(); + let (pat, predicate) = method_predicate(&call_expr, &var_name, &make); let (assist_id, message, text) = if method_kind == "is_some" { ("replace_is_some_with_if_let_some", "Replace `is_some` with `let Some`", "Some") @@ -61,13 +65,9 @@ pub(crate) fn replace_is_method_with_if_let_method( message, call_expr.syntax().text_range(), |edit| { - let make = SyntaxFactory::with_mappings(); let mut editor = edit.make_editor(call_expr.syntax()); - let var_pat = pat.unwrap_or_else(|| { - make.ident_pat(false, false, make.name(&var_name)).into() - }); - let pat = make.tuple_struct_pat(make.ident_path(text), [var_pat]).into(); + let pat = make.tuple_struct_pat(make.ident_path(text), [pat]).into(); let let_expr = make.expr_let(pat, receiver); if let Some(cap) = ctx.config.snippet_cap @@ -81,6 +81,7 @@ pub(crate) fn replace_is_method_with_if_let_method( let new_expr = if let Some(predicate) = predicate { let op = ast::BinaryOp::LogicOp(ast::LogicOp::And); + let predicate = wrap_paren(predicate, &make, ExprPrecedence::LAnd); make.expr_bin(let_expr.into(), op, predicate).into() } else { ast::Expr::from(let_expr) @@ -96,14 +97,23 @@ pub(crate) fn replace_is_method_with_if_let_method( } } -fn method_predicate(call_expr: &ast::MethodCallExpr) -> Option<(ast::Pat, ast::Expr)> { - let argument = call_expr.arg_list()?.args().next()?; - match argument { - ast::Expr::ClosureExpr(it) => { - let pat = it.param_list()?.params().next()?.pat()?; - Some((pat, it.body()?)) - } - _ => None, +fn method_predicate( + call_expr: &ast::MethodCallExpr, + name: &str, + make: &SyntaxFactory, +) -> (ast::Pat, Option) { + let argument = call_expr.arg_list().and_then(|it| it.args().next()); + if let Some(ast::Expr::ClosureExpr(it)) = argument.clone() + && let Some(pat) = it.param_list().and_then(|it| it.params().next()?.pat()) + { + (pat, it.body()) + } else { + let pat = make.ident_pat(false, false, make.name(name)); + let expr = argument.map(|expr| { + let arg_list = make.arg_list([make.expr_path(make.ident_path(name))]); + make.expr_call(wrap_paren_in_call(expr, make), arg_list).into() + }); + (pat.into(), expr) } } @@ -232,6 +242,54 @@ fn main() { let x = Some(1); if let Some(it) = x && it != 3 {} } +"#, + ); + + check_assist( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Some(1); + if x.is_som$0e_and(|it| it != 3 || it > 10) {} +} +"#, + r#" +fn main() { + let x = Some(1); + if let Some(it) = x && (it != 3 || it > 10) {} +} +"#, + ); + + check_assist( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Some(1); + if x.is_som$0e_and(predicate) {} +} +"#, + r#" +fn main() { + let x = Some(1); + if let Some(x1) = x && predicate(x1) {} +} +"#, + ); + + check_assist( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Some(1); + if x.is_som$0e_and(func.f) {} +} +"#, + r#" +fn main() { + let x = Some(1); + if let Some(x1) = x && (func.f)(x1) {} +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index 5587f1b59c542..6ff5f0bbd30cf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -86,8 +86,8 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> } fn let_expr_needs_paren(expr: &ast::Expr) -> bool { - let fake_expr_let = - ast::make::expr_let(ast::make::tuple_pat(None).into(), ast::make::ext::expr_unit()); + let make = SyntaxFactory::without_mappings(); + let fake_expr_let = make.expr_let(make.tuple_pat(None).into(), make.expr_unit()); let Some(fake_expr) = fake_expr_let.expr() else { stdx::never!(); return false; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 6ca3e26ca0187..6e4dd8cb73a6b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -2,10 +2,10 @@ use hir::Semantics; use ide_db::{RootDatabase, assists::AssistId, defs::Definition}; use syntax::{ AstNode, - ast::{self, Expr, HasArgList, make}, + ast::{self, Expr, HasArgList, make, syntax_factory::SyntaxFactory}, }; -use crate::{AssistContext, Assists}; +use crate::{AssistContext, Assists, utils::wrap_paren_in_call}; // Assist: replace_with_lazy_method // @@ -177,11 +177,7 @@ fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr { } })() .unwrap_or_else(|| { - let callable = if needs_parens_in_call(param) { - make::expr_paren(param.clone()).into() - } else { - param.clone() - }; + let callable = wrap_paren_in_call(param.clone(), &SyntaxFactory::without_mappings()); make::expr_call(callable, make::arg_list(Vec::new())).into() }) } @@ -200,12 +196,6 @@ fn ends_is(name: &str, end: &str) -> bool { name.strip_suffix(end).is_some_and(|s| s.is_empty() || s.ends_with('_')) } -fn needs_parens_in_call(param: &Expr) -> bool { - let call = make::expr_call(make::ext::expr_unit(), make::arg_list(Vec::new())); - let callable = call.expr().expect("invalid make call"); - param.needs_parens_in_place_of(call.syntax(), callable.syntax()) -} - #[cfg(test)] mod tests { use crate::tests::check_assist; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs index e4f5e3523bd2a..e029d7884fd52 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs @@ -45,6 +45,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option ast::LoopExpr(it) => it.syntax().clone(), ast::WhileExpr(it) => it.syntax().clone(), ast::MatchArm(it) => it.parent_match().syntax().clone(), + ast::LetElse(it) => it.syntax().parent()?, ast::LetStmt(it) => { replacement = wrap_let(&it, replacement); prefer_container = Some(it.syntax().clone()); @@ -556,6 +557,40 @@ fn main() { ); } + #[test] + fn simple_let_else() { + check_assist( + unwrap_block, + r#" +fn main() { + let Some(2) = None else {$0 + return; + }; +} +"#, + r#" +fn main() { + return; +} +"#, + ); + check_assist( + unwrap_block, + r#" +fn main() { + let Some(2) = None else {$0 + return + }; +} +"#, + r#" +fn main() { + return +} +"#, + ); + } + #[test] fn unwrap_match_arm() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs index 46f3e85e12346..e03274bbb3c9e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs @@ -1,3 +1,6 @@ +use std::iter; + +use either::Either; use syntax::{ AstNode, T, ast::{self, edit::AstNodeEdit}, @@ -24,11 +27,16 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let let_kw = ctx.find_token_syntax_at_offset(T![let])?; - let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?; - let indent_level = let_stmt.indent_level().0 as usize; - let pat = let_stmt.pat()?; - let ty = let_stmt.ty(); - let init = let_stmt.initializer()?; + let let_stmt = let_kw.parent().and_then(Either::::cast)?; + let mut indent_level = let_stmt.indent_level(); + let pat = either::for_both!(&let_stmt, it => it.pat())?; + let (ty, init, prefix, suffix) = match &let_stmt { + Either::Left(let_stmt) => (let_stmt.ty(), let_stmt.initializer()?, "", ";"), + Either::Right(let_expr) => { + indent_level += 1; + (None, let_expr.expr()?, "&& ", "") + } + }; // This only applies for tuple patterns, types, and initializers. let tuple_pat = match pat { @@ -60,25 +68,19 @@ pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option "Unwrap tuple", let_kw.text_range(), |edit| { - let indents = " ".repeat(indent_level); + let mut decls = String::new(); // If there is an ascribed type, insert that type for each declaration, // otherwise, omit that type. - if let Some(tys) = tuple_ty { - let mut zipped_decls = String::new(); - for (pat, ty, expr) in - itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields()) - { - zipped_decls.push_str(&format!("{indents}let {pat}: {ty} = {expr};\n")) - } - edit.replace(parent.text_range(), zipped_decls.trim()); - } else { - let mut zipped_decls = String::new(); - for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) { - zipped_decls.push_str(&format!("{indents}let {pat} = {expr};\n")); - } - edit.replace(parent.text_range(), zipped_decls.trim()); + let tys = + tuple_ty.into_iter().flat_map(|it| it.fields().map(Some)).chain(iter::repeat(None)); + for (pat, ty, expr) in itertools::izip!(tuple_pat.fields(), tys, tuple_init.fields()) { + let ty = ty.map_or_else(String::new, |ty| format!(": {ty}")); + decls.push_str(&format!("{prefix}let {pat}{ty} = {expr}{suffix}\n{indent_level}")) } + + let s = decls.trim(); + edit.replace(parent.text_range(), s.strip_prefix(prefix).unwrap_or(s)); }, ) } @@ -123,6 +125,28 @@ fn main() { ); } + #[test] + fn unwrap_tuples_in_let_expr() { + check_assist( + unwrap_tuple, + r#" +fn main() { + if $0let (foo, bar) = ("Foo", "Bar") { + code(); + } +} +"#, + r#" +fn main() { + if let foo = "Foo" + && let bar = "Bar" { + code(); + } +} +"#, + ); + } + #[test] fn unwrap_tuple_with_types() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 7d5740b748bef..36df4af31d5e2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -2,7 +2,7 @@ use ide_db::source_change::SourceChangeBuilder; use itertools::Itertools; use syntax::{ NodeOrToken, SyntaxToken, T, TextRange, algo, - ast::{self, AstNode, make, syntax_factory::SyntaxFactory}, + ast::{self, AstNode, edit::AstNodeEdit, make, syntax_factory::SyntaxFactory}, }; use crate::{AssistContext, AssistId, Assists}; @@ -27,7 +27,7 @@ use crate::{AssistContext, AssistId, Assists}; enum WrapUnwrapOption { WrapDerive { derive: TextRange, attr: ast::Attr }, - WrapAttr(ast::Attr), + WrapAttr(Vec), } /// Attempts to get the derive attribute from a derive attribute list @@ -102,9 +102,9 @@ fn attempt_get_derive(attr: ast::Attr, ident: SyntaxToken) -> WrapUnwrapOption { if ident.parent().and_then(ast::TokenTree::cast).is_none() || !attr.simple_name().map(|v| v.eq("derive")).unwrap_or_default() { - WrapUnwrapOption::WrapAttr(attr) + WrapUnwrapOption::WrapAttr(vec![attr]) } else { - attempt_attr().unwrap_or(WrapUnwrapOption::WrapAttr(attr)) + attempt_attr().unwrap_or_else(|| WrapUnwrapOption::WrapAttr(vec![attr])) } } pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { @@ -118,13 +118,27 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) - Some(attempt_get_derive(attr, ident)) } - (Some(attr), _) => Some(WrapUnwrapOption::WrapAttr(attr)), + (Some(attr), _) => Some(WrapUnwrapOption::WrapAttr(vec![attr])), _ => None, } } else { let covering_element = ctx.covering_element(); match covering_element { - NodeOrToken::Node(node) => ast::Attr::cast(node).map(WrapUnwrapOption::WrapAttr), + NodeOrToken::Node(node) => { + if let Some(attr) = ast::Attr::cast(node.clone()) { + Some(WrapUnwrapOption::WrapAttr(vec![attr])) + } else { + let attrs = node + .children() + .filter(|it| it.text_range().intersect(ctx.selection_trimmed()).is_some()) + .map(ast::Attr::cast) + .collect::>>()?; + if attrs.is_empty() { + return None; + } + Some(WrapUnwrapOption::WrapAttr(attrs)) + } + } NodeOrToken::Token(ident) if ident.kind() == syntax::T![ident] => { let attr = ident.parent_ancestors().find_map(ast::Attr::cast)?; Some(attempt_get_derive(attr, ident)) @@ -133,10 +147,12 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) - } }?; match option { - WrapUnwrapOption::WrapAttr(attr) if attr.simple_name().as_deref() == Some("cfg_attr") => { - unwrap_cfg_attr(acc, attr) - } - WrapUnwrapOption::WrapAttr(attr) => wrap_cfg_attr(acc, ctx, attr), + WrapUnwrapOption::WrapAttr(attrs) => match &attrs[..] { + [attr] if attr.simple_name().as_deref() == Some("cfg_attr") => { + unwrap_cfg_attr(acc, attrs.into_iter().next().unwrap()) + } + _ => wrap_cfg_attrs(acc, ctx, attrs), + }, WrapUnwrapOption::WrapDerive { derive, attr } => wrap_derive(acc, ctx, attr, derive), } } @@ -220,40 +236,51 @@ fn wrap_derive( ); Some(()) } -fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) -> Option<()> { - let range = attr.syntax().text_range(); - let path = attr.path()?; +fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec) -> Option<()> { + let (first_attr, last_attr) = (attrs.first()?, attrs.last()?); + let range = first_attr.syntax().text_range().cover(last_attr.syntax().text_range()); + let path_attrs = + attrs.iter().map(|attr| Some((attr.path()?, attr.clone()))).collect::>>()?; let handle_source_change = |edit: &mut SourceChangeBuilder| { let make = SyntaxFactory::with_mappings(); - let mut editor = edit.make_editor(attr.syntax()); - let mut raw_tokens = - vec![NodeOrToken::Token(make.token(T![,])), NodeOrToken::Token(make.whitespace(" "))]; - path.syntax().descendants_with_tokens().for_each(|it| { - if let NodeOrToken::Token(token) = it { - raw_tokens.push(NodeOrToken::Token(token)); - } - }); - if let Some(meta) = attr.meta() { - if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) { - raw_tokens.push(NodeOrToken::Token(make.whitespace(" "))); - raw_tokens.push(NodeOrToken::Token(eq)); - raw_tokens.push(NodeOrToken::Token(make.whitespace(" "))); + let mut editor = edit.make_editor(first_attr.syntax()); + let mut raw_tokens = vec![]; + for (path, attr) in path_attrs { + raw_tokens.extend([ + NodeOrToken::Token(make.token(T![,])), + NodeOrToken::Token(make.whitespace(" ")), + ]); + path.syntax().descendants_with_tokens().for_each(|it| { + if let NodeOrToken::Token(token) = it { + raw_tokens.push(NodeOrToken::Token(token)); + } + }); + if let Some(meta) = attr.meta() { + if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) { + raw_tokens.push(NodeOrToken::Token(make.whitespace(" "))); + raw_tokens.push(NodeOrToken::Token(eq)); + raw_tokens.push(NodeOrToken::Token(make.whitespace(" "))); - expr.syntax().descendants_with_tokens().for_each(|it| { - if let NodeOrToken::Token(token) = it { - raw_tokens.push(NodeOrToken::Token(token)); - } - }); - } else if let Some(tt) = meta.token_tree() { - raw_tokens.extend(tt.token_trees_and_tokens()); + expr.syntax().descendants_with_tokens().for_each(|it| { + if let NodeOrToken::Token(token) = it { + raw_tokens.push(NodeOrToken::Token(token)); + } + }); + } else if let Some(tt) = meta.token_tree() { + raw_tokens.extend(tt.token_trees_and_tokens()); + } } } let meta = make.meta_token_tree(make.ident_path("cfg_attr"), make.token_tree(T!['('], raw_tokens)); - let cfg_attr = - if attr.excl_token().is_some() { make.attr_inner(meta) } else { make.attr_outer(meta) }; + let cfg_attr = if first_attr.excl_token().is_some() { + make.attr_inner(meta) + } else { + make.attr_outer(meta) + }; - editor.replace(attr.syntax(), cfg_attr.syntax()); + let syntax_range = first_attr.syntax().clone().into()..=last_attr.syntax().clone().into(); + editor.replace_all(syntax_range, vec![cfg_attr.syntax().clone().into()]); if let Some(snippet_cap) = ctx.config.snippet_cap && let Some(first_meta) = @@ -332,7 +359,8 @@ fn unwrap_cfg_attr(acc: &mut Assists, attr: ast::Attr) -> Option<()> { return None; } let handle_source_change = |f: &mut SourceChangeBuilder| { - let inner_attrs = inner_attrs.iter().map(|it| it.to_string()).join("\n"); + let inner_attrs = + inner_attrs.iter().map(|it| it.to_string()).join(&format!("\n{}", attr.indent_level())); f.replace(range, inner_attrs); }; acc.add( @@ -414,6 +442,42 @@ mod tests { } "#, ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + pub struct Test { + #[other_attr] + $0#[foo] + #[bar]$0 + #[other_attr] + test: u32, + } + "#, + r#" + pub struct Test { + #[other_attr] + #[cfg_attr($0, foo, bar)] + #[other_attr] + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + pub struct Test { + #[cfg_attr(debug_assertions$0, foo, bar)] + test: u32, + } + "#, + r#" + pub struct Test { + #[foo] + #[bar] + test: u32, + } + "#, + ); } #[test] fn to_from_eq_attr() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 30405090002ab..8ba46799d5abd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -2282,7 +2282,7 @@ macro_rules! const_maker { }; } -trait ${0:NewTrait} { +trait ${0:Create} { // Used as an associated constant. const CONST_ASSOC: usize = N * 4; @@ -2291,7 +2291,7 @@ trait ${0:NewTrait} { const_maker! {i32, 7} } -impl ${0:NewTrait} for Foo { +impl ${0:Create} for Foo { // Used as an associated constant. const CONST_ASSOC: usize = N * 4; 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 b4055e77ccf8a..0657e7243af08 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -25,6 +25,7 @@ use syntax::{ edit::{AstNodeEdit, IndentLevel}, edit_in_place::AttrsOwnerEdit, make, + prec::ExprPrecedence, syntax_factory::SyntaxFactory, }, syntax_editor::{Element, Removable, SyntaxEditor}, @@ -86,17 +87,31 @@ pub fn extract_trivial_expression(block_expr: &ast::BlockExpr) -> Option ast::BlockExpr { +pub(crate) fn wrap_block(expr: &ast::Expr, make: &SyntaxFactory) -> ast::BlockExpr { if let ast::Expr::BlockExpr(block) = expr && let Some(first) = block.syntax().first_token() && first.kind() == T!['{'] { block.reset_indent() } else { - make::block_expr(None, Some(expr.reset_indent().indent(1.into()))) + make.block_expr(None, Some(expr.reset_indent().indent(1.into()))) } } +pub(crate) fn wrap_paren(expr: ast::Expr, make: &SyntaxFactory, prec: ExprPrecedence) -> ast::Expr { + if expr.precedence().needs_parentheses_in(prec) { make.expr_paren(expr).into() } else { expr } +} + +pub(crate) fn wrap_paren_in_call(expr: ast::Expr, make: &SyntaxFactory) -> ast::Expr { + if needs_parens_in_call(&expr) { make.expr_paren(expr).into() } else { expr } +} + +fn needs_parens_in_call(param: &ast::Expr) -> bool { + let call = make::expr_call(make::ext::expr_unit(), make::arg_list(Vec::new())); + let callable = call.expr().expect("invalid make call"); + param.needs_parens_in_place_of(call.syntax(), callable.syntax()) +} + /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as /// `#[test_case(...)]`, `#[tokio::test]` and similar. /// Also a regular `#[test]` annotation is supported. @@ -275,11 +290,6 @@ pub(crate) fn invert_boolean_expression(make: &SyntaxFactory, expr: ast::Expr) - invert_special_case(make, &expr).unwrap_or_else(|| make.expr_prefix(T![!], expr).into()) } -// FIXME: Migrate usages of this function to the above function and remove this. -pub(crate) fn invert_boolean_expression_legacy(expr: ast::Expr) -> ast::Expr { - invert_special_case_legacy(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into()) -} - fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option { match expr { ast::Expr::BinExpr(bin) => { @@ -343,62 +353,11 @@ fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option Option { - match expr { - ast::Expr::BinExpr(bin) => { - let bin = bin.clone_subtree(); - let op_token = bin.op_token()?; - let rev_token = match op_token.kind() { - T![==] => T![!=], - T![!=] => T![==], - T![<] => T![>=], - T![<=] => T![>], - T![>] => T![<=], - T![>=] => T![<], - // Parenthesize other expressions before prefixing `!` - _ => { - return Some( - make::expr_prefix(T![!], make::expr_paren(expr.clone()).into()).into(), - ); - } - }; - let mut bin_editor = SyntaxEditor::new(bin.syntax().clone()); - bin_editor.replace(op_token, make::token(rev_token)); - ast::Expr::cast(bin_editor.finish().new_root().clone()) - } - ast::Expr::MethodCallExpr(mce) => { - let receiver = mce.receiver()?; - let method = mce.name_ref()?; - let arg_list = mce.arg_list()?; - - let method = match method.text().as_str() { - "is_some" => "is_none", - "is_none" => "is_some", - "is_ok" => "is_err", - "is_err" => "is_ok", - _ => return None, - }; - Some(make::expr_method_call(receiver, make::name_ref(method), arg_list).into()) - } - ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => match pe.expr()? { - ast::Expr::ParenExpr(parexpr) => parexpr.expr(), - _ => pe.expr(), - }, - ast::Expr::Literal(lit) => match lit.kind() { - ast::LiteralKind::Bool(b) => match b { - true => Some(ast::Expr::Literal(make::expr_literal("false"))), - false => Some(ast::Expr::Literal(make::expr_literal("true"))), - }, - _ => None, - }, - _ => None, - } -} - pub(crate) fn insert_attributes( before: impl Element, edit: &mut SyntaxEditor, attrs: impl IntoIterator, + make: &SyntaxFactory, ) { let mut attrs = attrs.into_iter().peekable(); if attrs.peek().is_none() { @@ -410,9 +369,7 @@ pub(crate) fn insert_attributes( edit.insert_all( syntax::syntax_editor::Position::before(elem), attrs - .flat_map(|attr| { - [attr.syntax().clone().into(), make::tokens::whitespace(&whitespace).into()] - }) + .flat_map(|attr| [attr.syntax().clone().into(), make.whitespace(&whitespace).into()]) .collect(), ); } @@ -1095,18 +1052,21 @@ pub(crate) fn trimmed_text_range(source_file: &SourceFile, initial_range: TextRa /// Convert a list of function params to a list of arguments that can be passed /// into a function call. -pub(crate) fn convert_param_list_to_arg_list(list: ast::ParamList) -> ast::ArgList { +pub(crate) fn convert_param_list_to_arg_list( + list: ast::ParamList, + make: &SyntaxFactory, +) -> ast::ArgList { let mut args = vec![]; for param in list.params() { if let Some(ast::Pat::IdentPat(pat)) = param.pat() && let Some(name) = pat.name() { let name = name.to_string(); - let expr = make::expr_path(make::ext::ident_path(&name)); + let expr = make.expr_path(make.ident_path(&name)); args.push(expr); } } - make::arg_list(args) + make.arg_list(args) } /// Calculate the number of hashes required for a raw string containing `s` @@ -1191,7 +1151,10 @@ pub(crate) fn replace_record_field_expr( /// Creates a token tree list from a syntax node, creating the needed delimited sub token trees. /// Assumes that the input syntax node is a valid syntax tree. -pub(crate) fn tt_from_syntax(node: SyntaxNode) -> Vec> { +pub(crate) fn tt_from_syntax( + node: SyntaxNode, + make: &SyntaxFactory, +) -> Vec> { let mut tt_stack = vec![(None, vec![])]; for element in node.descendants_with_tokens() { @@ -1219,7 +1182,7 @@ pub(crate) fn tt_from_syntax(node: SyntaxNode) -> Vec { @@ -1254,6 +1217,20 @@ pub(crate) fn cover_let_chain(mut expr: ast::Expr, range: TextRange) -> Option std::ops::RangeInclusive { + let node = match source.syntax().covering_element(range) { + NodeOrToken::Node(node) => node, + NodeOrToken::Token(t) => t.parent().unwrap(), + }; + let mut iter = node.children_with_tokens().filter(|it| range.contains_range(it.text_range())); + let first = iter.next().unwrap_or(node.into()); + let last = iter.last().unwrap_or_else(|| first.clone()); + first..=last +} + pub(crate) fn is_selected( it: &impl AstNode, selection: syntax::TextRange, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs index df8ad4111234e..fc9bf210e4872 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs @@ -5,7 +5,7 @@ //! based on the parent of the existing expression. use syntax::{ AstNode, T, - ast::{self, FieldExpr, MethodCallExpr, make, syntax_factory::SyntaxFactory}, + ast::{self, FieldExpr, MethodCallExpr, syntax_factory::SyntaxFactory}, }; use crate::AssistContext; @@ -119,13 +119,13 @@ pub(crate) struct RefData { impl RefData { /// Derefs `expr` and wraps it in parens if necessary - pub(crate) fn wrap_expr(&self, mut expr: ast::Expr) -> ast::Expr { + pub(crate) fn wrap_expr(&self, mut expr: ast::Expr, make: &SyntaxFactory) -> ast::Expr { if self.needs_deref { - expr = make::expr_prefix(T![*], expr).into(); + expr = make.expr_prefix(T![*], expr).into(); } if self.needs_parentheses { - expr = make::expr_paren(expr).into(); + expr = make.expr_paren(expr).into(); } expr diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index 96dac66b8a199..bd0b69215cf7a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs @@ -30,14 +30,27 @@ pub(crate) fn complete_fn_param( _ => return None, }; + let qualifier = param_qualifier(param); let comma_wrapper = comma_wrapper(ctx); let mut add_new_item_to_acc = |label: &str| { - let mk_item = |label: &str, range: TextRange| { - CompletionItem::new(CompletionItemKind::Binding, range, label, ctx.edition) + let label = label.strip_prefix(qualifier.as_str()).unwrap_or(label); + let insert = if label.starts_with('#') { + // FIXME: `#[attr] it: i32` -> `#[attr] mut it: i32` + label.to_smolstr() + } else { + format_smolstr!("{qualifier}{label}") + }; + let mk_item = |insert_text: &str, range: TextRange| { + let mut item = + CompletionItem::new(CompletionItemKind::Binding, range, label, ctx.edition); + if insert_text != label { + item.insert_text(insert_text); + } + item }; let item = match &comma_wrapper { - Some((fmt, range)) => mk_item(&fmt(label), *range), - None => mk_item(label, ctx.source_range()), + Some((fmt, range)) => mk_item(&fmt(&insert), *range), + None => mk_item(&insert, ctx.source_range()), }; // Completion lookup is omitted intentionally here. // See the full discussion: https://github.com/rust-lang/rust-analyzer/issues/12073 @@ -75,9 +88,6 @@ fn fill_fn_params( let mut file_params = FxHashMap::default(); let mut extract_params = |f: ast::Fn| { - if !is_simple_param(current_param) { - return; - } f.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| { if let Some(pat) = param.pat() { let whole_param = param.to_smolstr(); @@ -88,6 +98,9 @@ fn fill_fn_params( }; for node in ctx.token.parent_ancestors() { + if !is_simple_param(current_param) { + break; + } match_ast! { match node { ast::SourceFile(it) => it.items().filter_map(|item| match item { @@ -214,3 +227,16 @@ fn is_simple_param(param: &ast::Param) -> bool { .pat() .is_none_or(|pat| matches!(pat, ast::Pat::IdentPat(ident_pat) if ident_pat.pat().is_none())) } + +fn param_qualifier(param: &ast::Param) -> SmolStr { + let mut b = syntax::SmolStrBuilder::new(); + if let Some(ast::Pat::IdentPat(pat)) = param.pat() { + if pat.ref_token().is_some() { + b.push_str("ref "); + } + if pat.mut_token().is_some() { + b.push_str("mut "); + } + } + b.finish() +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index ea53aef40c2e7..5b91e7c456a53 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -16,7 +16,7 @@ use itertools::Itertools; use stdx::never; use syntax::{ SmolStr, - SyntaxKind::{EXPR_STMT, STMT_LIST}, + SyntaxKind::{CLOSURE_EXPR, EXPR_STMT, MATCH_ARM, STMT_LIST}, T, TextRange, TextSize, ToSmolStr, ast::{self, AstNode, AstToken}, format_smolstr, match_ast, @@ -66,6 +66,12 @@ pub(crate) fn complete_postfix( Some(it) => it, None => return, }; + let semi = + if expr_ctx.in_block_expr && ctx.token.next_token().is_none_or(|it| it.kind() != T![;]) { + ";" + } else { + "" + }; let cfg = ctx.config.find_path_config(ctx.is_nightly); @@ -151,12 +157,12 @@ pub(crate) fn complete_postfix( .add_to(acc, ctx.db); } _ if matches!(parent.kind(), STMT_LIST | EXPR_STMT) => { - postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) + postfix_snippet("let", "let", &format!("let $0 = {receiver_text}{semi}")) .add_to(acc, ctx.db); - postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) + postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text}{semi}")) .add_to(acc, ctx.db); } - _ if ast::MatchArm::can_cast(parent.kind()) => { + _ if matches!(parent.kind(), MATCH_ARM | CLOSURE_EXPR) => { postfix_snippet( "let", "let", @@ -307,26 +313,12 @@ pub(crate) fn complete_postfix( add_format_like_completions(acc, ctx, &dot_receiver_including_refs, cap, &literal_text); } - postfix_snippet( - "return", - "return expr", - &format!( - "return {receiver_text}{semi}", - semi = if expr_ctx.in_block_expr { ";" } else { "" } - ), - ) - .add_to(acc, ctx.db); + postfix_snippet("return", "return expr", &format!("return {receiver_text}{semi}")) + .add_to(acc, ctx.db); if let Some(BreakableKind::Block | BreakableKind::Loop) = expr_ctx.in_breakable { - postfix_snippet( - "break", - "break expr", - &format!( - "break {receiver_text}{semi}", - semi = if expr_ctx.in_block_expr { ";" } else { "" } - ), - ) - .add_to(acc, ctx.db); + postfix_snippet("break", "break expr", &format!("break {receiver_text}{semi}")) + .add_to(acc, ctx.db); } } @@ -371,12 +363,20 @@ fn get_receiver_text( range.range = TextRange::at(range.range.start(), range.range.len() - TextSize::of('.')) } let file_text = sema.db.file_text(range.file_id.file_id(sema.db)); - let mut text = file_text.text(sema.db)[range.range].to_owned(); + let text = file_text.text(sema.db); + let indent_spaces = indent_of_tail_line(&text[TextRange::up_to(range.range.start())]); + let mut text = stdx::dedent_by(indent_spaces, &text[range.range]); // The receiver texts should be interpreted as-is, as they are expected to be // normal Rust expressions. escape_snippet_bits(&mut text); - text + return text; + + fn indent_of_tail_line(text: &str) -> usize { + let tail_line = text.rsplit_once('\n').map_or(text, |(_, s)| s); + let trimmed = tail_line.trim_start_matches(' '); + tail_line.len() - trimmed.len() + } } /// Escapes `\` and `$` so that they don't get interpreted as snippet-specific constructs. @@ -402,6 +402,10 @@ fn receiver_accessor(receiver: &ast::Expr) -> ast::Expr { .unwrap_or_else(|| receiver.clone()) } +/// Given an `initial_element`, tries to expand it to include deref(s), and then references. +/// Returns the expanded expressions, and the added prefix as a string +/// +/// For example, if called with the `42` in `&&mut *42`, would return `(&&mut *42, "&&mut *")`. fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) { let mut resulting_element = initial_element.clone(); let mut prefix = String::new(); @@ -410,11 +414,8 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) { while let Some(parent_deref_element) = resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast) + && parent_deref_element.op_kind() == Some(ast::UnaryOp::Deref) { - if parent_deref_element.op_kind() != Some(ast::UnaryOp::Deref) { - break; - } - found_ref_or_deref = true; resulting_element = ast::Expr::from(parent_deref_element); @@ -663,6 +664,22 @@ fn main() { #[test] fn let_middle_block() { + check_edit( + "let", + r#" +fn main() { + baz.l$0 + res +} +"#, + r#" +fn main() { + let $0 = baz; + res +} +"#, + ); + check( r#" fn main() { @@ -719,6 +736,20 @@ fn main() { #[test] fn let_tail_block() { + check_edit( + "let", + r#" +fn main() { + baz.l$0 +} +"#, + r#" +fn main() { + let $0 = baz; +} +"#, + ); + check( r#" fn main() { @@ -772,6 +803,23 @@ fn main() { ); } + #[test] + fn let_before_semicolon() { + check_edit( + "let", + r#" +fn main() { + baz.l$0; +} +"#, + r#" +fn main() { + let $0 = baz; +} +"#, + ); + } + #[test] fn option_iflet() { check_edit( @@ -965,6 +1013,28 @@ fn main() { ); } + #[test] + fn closure_let_block() { + check_edit( + "let", + r#" +fn main() { + let bar = 2; + let f = || bar.$0; +} +"#, + r#" +fn main() { + let bar = 2; + let f = || { + let $1 = bar; + $0 +}; +} +"#, + ); + } + #[test] fn option_letelse() { check_edit( @@ -1040,6 +1110,7 @@ fn main() { #[test] fn postfix_completion_for_references() { check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#); + check_edit("dbg", r#"fn main() { &&*"hello".$0 }"#, r#"fn main() { dbg!(&&*"hello") }"#); check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#); check_edit( "ifl", @@ -1198,9 +1269,9 @@ use core::ops::ControlFlow; fn main() { ControlFlow::Break(match true { - true => "\${1:placeholder}", - false => "\\\$", - }) + true => "\${1:placeholder}", + false => "\\\$", +}) } "#, ); @@ -1440,4 +1511,31 @@ fn foo() { "#, ); } + + #[test] + fn snippet_dedent() { + check_edit( + "let", + r#" +//- minicore: option +fn foo(x: Option, y: Option) { + let _f = || { + x + .and(y) + .map(|it| it+2) + .$0 + }; +} +"#, + r#" +fn foo(x: Option, y: Option) { + let _f = || { + let $0 = x + .and(y) + .map(|it| it+2); + }; +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 4b0cc0c7cd98e..bf899539a20ba 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -778,6 +778,16 @@ fn expected_type_and_name<'db>( let ty = sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original); (ty, None) }, + ast::TupleStructPat(it) => { + let fields = it.path().and_then(|path| match sema.resolve_path(&path)? { + hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) => Some(adt.as_struct()?.fields(sema.db)), + hir::PathResolution::Def(hir::ModuleDef::Variant(variant)) => Some(variant.fields(sema.db)), + _ => None, + }); + let nr = it.fields().take_while(|it| it.syntax().text_range().end() <= token.text_range().start()).count(); + let ty = fields.and_then(|fields| Some(fields.get(nr)?.ty(sema.db).to_type(sema.db))); + (ty, None) + }, ast::Fn(it) => { cov_mark::hit!(expected_type_fn_ret_with_leading_char); cov_mark::hit!(expected_type_fn_ret_without_leading_char); @@ -944,10 +954,10 @@ fn classify_name_ref<'db>( let field_expr_handle = |receiver, node| { let receiver = find_opt_node_in_file(original_file, receiver); let receiver_is_ambiguous_float_literal = match &receiver { - Some(ast::Expr::Literal(l)) => matches! { - l.kind(), - ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().is_some_and(|it| it.text().ends_with('.')) - }, + Some(ast::Expr::Literal(l)) => { + matches!(l.kind(), ast::LiteralKind::FloatNumber { .. }) + && l.syntax().last_token().is_some_and(|it| it.text().ends_with('.')) + } _ => false, }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index e97d9720e3f34..94d904932ac56 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -287,6 +287,50 @@ fn foo() -> Foo { ); } +#[test] +fn expected_type_tuple_struct_pat() { + check_expected_type_and_name( + r#" +//- minicore: option +struct Foo(Option); +fn foo(x: Foo) -> Foo { + match x { Foo($0) => () } +} +"#, + expect![[r#"ty: Option, name: ?"#]], + ); + + check_expected_type_and_name( + r#" +struct Foo(i32, u32, f32); +fn foo(x: Foo) -> Foo { + match x { Foo($0) => () } +} +"#, + expect![[r#"ty: i32, name: ?"#]], + ); + + check_expected_type_and_name( + r#" +struct Foo(i32, u32, f32); +fn foo(x: Foo) -> Foo { + match x { Foo(num,$0) => () } +} +"#, + expect![[r#"ty: u32, name: ?"#]], + ); + + check_expected_type_and_name( + r#" +struct Foo(i32, u32, f32); +fn foo(x: Foo) -> Foo { + match x { Foo(num,$0,float) => () } +} +"#, + expect![[r#"ty: u32, name: ?"#]], + ); +} + #[test] fn expected_type_if_let_without_leading_char() { cov_mark::check!(expected_type_if_let_without_leading_char); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 475e00dfcf29b..dfa30841e7db1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -678,7 +678,7 @@ fn main() { fn complete_fn_param() { // has mut kw check_edit( - "mut bar: u32", + "bar: u32", r#" fn f(foo: (), mut bar: u32) {} fn g(foo: (), mut ba$0) @@ -689,10 +689,35 @@ fn g(foo: (), mut bar: u32) "#, ); - // has type param + // has unmatched mut kw + check_edit( + "bar: u32", + r#" +fn f(foo: (), bar: u32) {} +fn g(foo: (), mut ba$0) +"#, + r#" +fn f(foo: (), bar: u32) {} +fn g(foo: (), mut bar: u32) +"#, + ); + check_edit( "mut bar: u32", r#" +fn f(foo: (), mut bar: u32) {} +fn g(foo: (), ba$0) +"#, + r#" +fn f(foo: (), mut bar: u32) {} +fn g(foo: (), mut bar: u32) +"#, + ); + + // has type param + check_edit( + "bar: u32", + r#" fn g(foo: (), mut ba$0: u32) fn f(foo: (), mut bar: u32) {} "#, @@ -707,7 +732,7 @@ fn f(foo: (), mut bar: u32) {} fn complete_fn_mut_param_add_comma() { // add leading and trailing comma check_edit( - ", mut bar: u32,", + "bar: u32", r#" fn f(foo: (), mut bar: u32) {} fn g(foo: ()mut ba$0 baz: ()) @@ -746,7 +771,7 @@ fn g(foo: (), #[baz = "qux"] mut bar: u32) ); check_edit( - r#", #[baz = "qux"] mut bar: u32"#, + r#"#[baz = "qux"] mut bar: u32"#, r#" fn f(foo: (), #[baz = "qux"] mut bar: u32) {} fn g(foo: ()#[baz = "qux"] mut ba$0) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs index d6d73da3f140b..aaa225642c6f8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs @@ -43,7 +43,7 @@ fn bar(file_id: usize) {} fn baz(file$0 id: u32) {} "#, expect![[r#" - bn file_id: usize, + bn file_id: usize kw mut kw ref "#]], 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 bcfe3a8aa5ce1..619bb2307cd4b 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 @@ -97,25 +97,37 @@ fn missing_record_expr_field_fixes( make::ty(&new_field_type.display_source_code(sema.db, module.into(), true).ok()?), ); - let last_field = record_fields.fields().last()?; - let last_field_syntax = last_field.syntax(); - let indent = IndentLevel::from_node(last_field_syntax); + let (indent, offset, postfix, needs_comma) = + if let Some(last_field) = record_fields.fields().last() { + let indent = IndentLevel::from_node(last_field.syntax()); + let offset = last_field.syntax().text_range().end(); + let needs_comma = !last_field.to_string().ends_with(','); + (indent, offset, String::new(), needs_comma) + } else { + let indent = IndentLevel::from_node(record_fields.syntax()); + let offset = record_fields.l_curly_token()?.text_range().end(); + let postfix = if record_fields.syntax().text().contains_char('\n') { + ",".into() + } else { + format!(",\n{indent}") + }; + (indent + 1, offset, postfix, false) + }; let mut new_field = new_field.to_string(); // FIXME: check submodule instead of FileId if usage_file_id != def_file_id && !matches!(def_id, hir::VariantDef::Variant(_)) { new_field = format!("pub(crate) {new_field}"); } - new_field = format!("\n{indent}{new_field}"); + new_field = format!("\n{indent}{new_field}{postfix}"); - let needs_comma = !last_field_syntax.to_string().ends_with(','); if needs_comma { new_field = format!(",{new_field}"); } let source_change = SourceChange::from_text_edit( def_file_id.file_id(sema.db), - TextEdit::insert(last_field_syntax.text_range().end(), new_field), + TextEdit::insert(offset, new_field), ); return Some(vec![fix( @@ -334,6 +346,44 @@ struct Foo { ) } + #[test] + fn test_add_field_from_usage_with_empty_struct() { + check_fix( + r" +fn main() { + Foo { bar$0: false }; +} +struct Foo {} +", + r" +fn main() { + Foo { bar: false }; +} +struct Foo { + bar: bool, +} +", + ); + + check_fix( + r" +fn main() { + Foo { bar$0: false }; +} +struct Foo { +} +", + r" +fn main() { + Foo { bar: false }; +} +struct Foo { + bar: bool, +} +", + ); + } + #[test] fn test_add_field_in_other_file_from_usage() { check_fix( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index c86ecd2f03b93..bc10e82854f5c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs @@ -1,4 +1,11 @@ -use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; +use either::Either; +use hir::Semantics; +use ide_db::text_edit::TextEdit; +use ide_db::ty_filter::TryEnum; +use ide_db::{RootDatabase, source_change::SourceChange}; +use syntax::{AstNode, ast}; + +use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // Diagnostic: non-exhaustive-let // @@ -15,11 +22,74 @@ pub(crate) fn non_exhaustive_let( d.pat.map(Into::into), ) .stable() + .with_fixes(fixes(&ctx.sema, d)) +} + +fn fixes(sema: &Semantics<'_, RootDatabase>, d: &hir::NonExhaustiveLet) -> Option> { + let root = sema.parse_or_expand(d.pat.file_id); + let pat = d.pat.value.to_node(&root); + let let_stmt = ast::LetStmt::cast(pat.syntax().parent()?)?; + let early_node = + sema.ancestors_with_macros(let_stmt.syntax().clone()).find_map(AstNode::cast)?; + let early_text = early_text(sema, &early_node); + + if let_stmt.let_else().is_some() { + return None; + } + let hir::FileRangeWrapper { file_id, range } = sema.original_range_opt(let_stmt.syntax())?; + let insert_offset = if let Some(semicolon) = let_stmt.semicolon_token() + && let Some(token) = sema.parse(file_id).syntax().token_at_offset(range.end()).left_biased() + && token.kind() == semicolon.kind() + { + token.text_range().start() + } else { + range.end() + }; + let semicolon = if let_stmt.semicolon_token().is_none() { ";" } else { "" }; + let else_block = format!(" else {{ {early_text} }}{semicolon}"); + let file_id = file_id.file_id(sema.db); + + let source_change = + SourceChange::from_text_edit(file_id, TextEdit::insert(insert_offset, else_block)); + let target = sema.original_range(let_stmt.syntax()).range; + Some(vec![fix("add_let_else_block", "Add let-else block", source_change, target)]) +} + +fn early_text( + sema: &Semantics<'_, RootDatabase>, + early_node: &Either>, +) -> &'static str { + match early_node { + Either::Left(_any_loop) => "continue", + Either::Right(Either::Left(fn_)) => sema + .to_def(fn_) + .map(|fn_def| fn_def.ret_type(sema.db)) + .map(|ty| return_text(&ty, sema)) + .unwrap_or("return"), + Either::Right(Either::Right(closure)) => closure + .body() + .and_then(|expr| sema.type_of_expr(&expr)) + .map(|ty| return_text(&ty.adjusted(), sema)) + .unwrap_or("return"), + } +} + +fn return_text(ty: &hir::Type<'_>, sema: &Semantics<'_, RootDatabase>) -> &'static str { + if ty.is_unit() { + "return" + } else if let Some(try_enum) = TryEnum::from_ty(sema, ty) { + match try_enum { + TryEnum::Option => "return None", + TryEnum::Result => "return Err($0)", + } + } else { + "return $0" + } } #[cfg(test)] mod tests { - use crate::tests::check_diagnostics; + use crate::tests::{check_diagnostics, check_fix}; #[test] fn option_nonexhaustive() { @@ -28,7 +98,7 @@ mod tests { //- minicore: option fn main() { let None = Some(5); - //^^^^ error: non-exhaustive pattern: `Some(_)` not covered + //^^^^ 💡 error: non-exhaustive pattern: `Some(_)` not covered } "#, ); @@ -54,7 +124,7 @@ fn main() { fn main() { '_a: { let None = Some(5); - //^^^^ error: non-exhaustive pattern: `Some(_)` not covered + //^^^^ 💡 error: non-exhaustive pattern: `Some(_)` not covered } } "#, @@ -66,7 +136,7 @@ fn main() { fn main() { let _ = async { let None = Some(5); - //^^^^ error: non-exhaustive pattern: `Some(_)` not covered + //^^^^ 💡 error: non-exhaustive pattern: `Some(_)` not covered }; } "#, @@ -78,7 +148,7 @@ fn main() { fn main() { unsafe { let None = Some(5); - //^^^^ error: non-exhaustive pattern: `Some(_)` not covered + //^^^^ 💡 error: non-exhaustive pattern: `Some(_)` not covered } } "#, @@ -101,7 +171,7 @@ fn test(x: Result) { //- minicore: result fn test(x: Result) { let Ok(_y) = x; - //^^^^^^ error: non-exhaustive pattern: `Err(_)` not covered + //^^^^^^ 💡 error: non-exhaustive pattern: `Err(_)` not covered } "#, ); @@ -132,6 +202,136 @@ fn foo(v: Enum<()>) { ); } + #[test] + fn fix_return_in_loop() { + check_fix( + r#" +//- minicore: option +fn foo() { + while cond { + let None$0 = Some(5); + } +} +"#, + r#" +fn foo() { + while cond { + let None = Some(5) else { continue }; + } +} +"#, + ); + } + + #[test] + fn fix_return_in_fn() { + check_fix( + r#" +//- minicore: option +fn foo() { + let None$0 = Some(5); +} +"#, + r#" +fn foo() { + let None = Some(5) else { return }; +} +"#, + ); + } + + #[test] + fn fix_return_in_macro_expanded() { + check_fix( + r#" +//- minicore: option +macro_rules! identity { ($($t:tt)*) => { $($t)* }; } +fn foo() { + identity! { + let None$0 = Some(5); + } +} +"#, + r#" +macro_rules! identity { ($($t:tt)*) => { $($t)* }; } +fn foo() { + identity! { + let None = Some(5) else { return }; + } +} +"#, + ); + } + + #[test] + fn fix_return_in_incomplete_let() { + check_fix( + r#" +//- minicore: option +fn foo() { + let None$0 = Some(5) +} +"#, + r#" +fn foo() { + let None = Some(5) else { return }; +} +"#, + ); + } + + #[test] + fn fix_return_in_closure() { + check_fix( + r#" +//- minicore: option +fn foo() -> Option<()> { + let _f = || { + let None$0 = Some(5); + }; +} +"#, + r#" +fn foo() -> Option<()> { + let _f = || { + let None = Some(5) else { return }; + }; +} +"#, + ); + } + + #[test] + fn fix_return_try_in_fn() { + check_fix( + r#" +//- minicore: option +fn foo() -> Option<()> { + let None$0 = Some(5); +} +"#, + r#" +fn foo() -> Option<()> { + let None = Some(5) else { return None }; +} +"#, + ); + + check_fix( + r#" +//- minicore: option, result +fn foo() -> Result<(), i32> { + let None$0 = Some(5); +} +"#, + r#" +fn foo() -> Result<(), i32> { + let None = Some(5) else { return Err($0) }; +} +"#, + ); + } + #[test] fn regression_20259() { check_diagnostics( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs index 7dc5b5b45e5f4..04f48ae3db170 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs @@ -48,7 +48,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option block diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 95fcfce291277..8753eab43a8ca 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -42,6 +42,7 @@ pub struct LoadCargoConfig { pub load_out_dirs_from_check: bool, pub with_proc_macro_server: ProcMacroServerChoice, pub prefill_caches: bool, + pub num_worker_threads: usize, pub proc_macro_processes: usize, } @@ -197,7 +198,7 @@ pub fn load_workspace_into_db( ); if load_config.prefill_caches { - prime_caches::parallel_prime_caches(db, 1, &|_| ()); + prime_caches::parallel_prime_caches(db, load_config.num_worker_threads, &|_| ()); } Ok((vfs, proc_macro_server.and_then(Result::ok))) @@ -744,16 +745,26 @@ mod tests { #[test] fn test_loading_rust_analyzer() { - let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); + let cargo_toml_path = Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .parent() + .unwrap() + .join("Cargo.toml"); + let cargo_toml_path = AbsPathBuf::assert_utf8(cargo_toml_path); + let manifest = ProjectManifest::from_manifest_file(cargo_toml_path).unwrap(); + let cargo_config = CargoConfig { set_test: true, ..CargoConfig::default() }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: false, with_proc_macro_server: ProcMacroServerChoice::None, prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: 1, }; + let workspace = ProjectWorkspace::load(manifest, &cargo_config, &|_| {}).unwrap(); let (db, _vfs, _proc_macro) = - load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {}).unwrap(); + load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config).unwrap(); let n_crates = db.all_crates().len(); // RA has quite a few crates, but the exact count doesn't matter diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 9b9111012b541..4ea136afbb455 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -365,9 +365,27 @@ pub enum RunnableKind { /// May include {test_id} which will get the test clicked on by the user. TestOne, + /// Run tests matching a pattern (in RA, usually a path::to::module::of::tests) + /// May include {label} which will get the label from the `build` section of a crate. + /// May include {test_pattern} which will get the test module clicked on by the user. + TestMod, + + /// Run a single doctest + /// May include {label} which will get the label from the `build` section of a crate. + /// May include {test_id} which will get the doctest clicked on by the user. + DocTestOne, + + /// Run a single benchmark + /// May include {label} which will get the label from the `build` section of a crate. + /// May include {bench_id} which will get the benchmark clicked on by the user. + BenchOne, + /// Template for checking a target, emitting rustc JSON diagnostics. /// May include {label} which will get the label from the `build` section of a crate. Flycheck, + + /// For forwards-compatibility, i.e. old rust-analyzer binary with newer workspace discovery tools + Unknown, } #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] @@ -380,6 +398,8 @@ pub struct ProjectJsonData { crates: Vec, #[serde(default)] runnables: Vec, + // + // New fields should be Option or #[serde(default)]. This applies to most of this datastructure. } #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Default)] @@ -453,32 +473,40 @@ enum EditionData { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub struct BuildData { +struct BuildData { label: String, build_file: Utf8PathBuf, target_kind: TargetKindData, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct RunnableData { - pub program: String, - pub args: Vec, - pub cwd: Utf8PathBuf, - pub kind: RunnableKindData, +struct RunnableData { + program: String, + args: Vec, + cwd: Utf8PathBuf, + kind: RunnableKindData, } #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub enum RunnableKindData { +enum RunnableKindData { Flycheck, Check, Run, TestOne, + TestMod, + DocTestOne, + BenchOne, + + /// For forwards-compatibility, i.e. old rust-analyzer binary with newer workspace discovery tools + #[allow(unused)] + #[serde(other)] + Unknown, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub enum TargetKindData { +enum TargetKindData { Bin, /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...). Lib, @@ -541,7 +569,11 @@ impl From for RunnableKind { RunnableKindData::Check => RunnableKind::Check, RunnableKindData::Run => RunnableKind::Run, RunnableKindData::TestOne => RunnableKind::TestOne, + RunnableKindData::TestMod => RunnableKind::TestMod, + RunnableKindData::DocTestOne => RunnableKind::DocTestOne, + RunnableKindData::BenchOne => RunnableKind::BenchOne, RunnableKindData::Flycheck => RunnableKind::Flycheck, + RunnableKindData::Unknown => RunnableKind::Unknown, } } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index a03ed562e1be5..395cea6f76e61 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -192,6 +192,12 @@ fn rust_project_hello_world_project_model() { ); } +#[test] +fn rust_project_labeled_project_model() { + // This just needs to parse. + _ = load_rust_project("labeled-project.json"); +} + #[test] fn rust_project_cfg_groups() { let (crate_graph, _proc_macros) = load_rust_project("cfg-groups.json"); diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/labeled-project.json b/src/tools/rust-analyzer/crates/project-model/test_data/labeled-project.json new file mode 100644 index 0000000000000..5c0e1f33979ec --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/labeled-project.json @@ -0,0 +1,37 @@ +{ + "sysroot_src": null, + "crates": [ + { + "display_name": "hello_world", + "root_module": "$ROOT$src/lib.rs", + "edition": "2018", + "deps": [], + "is_workspace_member": true, + "build": { + "label": "//:hello_world", + "build_file": "$ROOT$BUILD", + "target_kind": "bin" + } + } + ], + "runnables": [ + { + "kind": "run", + "program": "bazel", + "args": ["run", "{label}"], + "cwd": "$ROOT$" + }, + { + "kind": "flycheck", + "program": "$ROOT$custom-flychecker.sh", + "args": ["{label}"], + "cwd": "$ROOT$" + }, + { + "kind": "we-ignore-unknown-runnable-kinds-for-forwards-compatibility", + "program": "abc", + "args": ["{label}"], + "cwd": "$ROOT$" + } + ] +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 82f04aa78ebb7..ad1cca08cb579 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -91,6 +91,7 @@ impl flags::AnalysisStats { } }, prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: 1, }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index 575c77f8428ca..efbaad3c4936e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -41,6 +41,7 @@ impl flags::Diagnostics { load_out_dirs_from_check: !self.disable_build_scripts, with_proc_macro_server, prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: 1, }; let (db, _vfs, _proc_macro) = diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index d68f7ab5b7b24..03849938f5984 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -191,6 +191,9 @@ xflags::xflags! { /// Exclude code from vendored libraries from the resulting index. optional --exclude-vendored-libraries + + /// The number of worker threads for cache priming. Defaults to the number of physical cores. + optional --num-threads num_threads: usize } } } @@ -338,6 +341,7 @@ pub struct Scip { pub output: Option, pub config_path: Option, pub exclude_vendored_libraries: bool, + pub num_threads: Option, } impl RustAnalyzer { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index e5e238db63618..3950a581fd776 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -293,6 +293,7 @@ impl flags::Lsif { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: 1, }; let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(self.path)); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs index d5da6791797b8..beedcfae4ed85 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs @@ -38,6 +38,7 @@ impl flags::PrimeCaches { // we want to ensure that this command, not `load_workspace_at`, // is responsible for that work. prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: config.proc_macro_num_processes(), }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs index d4a56d773e7de..e8c88cadf6f0a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs @@ -23,6 +23,7 @@ impl flags::RunTests { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: 1, }; let (ref db, _vfs, _proc_macro) = diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index e8c6c5f4d4f70..49f28352b6cf6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -103,6 +103,7 @@ impl Tester { load_out_dirs_from_check: false, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: 1, }; let (db, _vfs, _proc_macro) = diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index ed0476697c9cc..ef6d4399e663c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -52,6 +52,7 @@ impl flags::Scip { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, + num_worker_threads: self.num_threads.unwrap_or_else(num_cpus::get_physical), proc_macro_processes: config.proc_macro_num_processes(), }; let cargo_config = config.cargo(None); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs index 6bc0792daabbd..7b00aebbfc4a5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs @@ -20,6 +20,7 @@ impl flags::Ssr { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: 1, }; let (ref db, vfs, _proc_macro) = load_workspace_at( @@ -57,6 +58,7 @@ impl flags::Search { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: 1, }; let (ref db, _vfs, _proc_macro) = load_workspace_at( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs index 49c6fcb91ebfc..2d9b870f4de82 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -44,6 +44,7 @@ impl flags::UnresolvedReferences { load_out_dirs_from_check: !self.disable_build_scripts, with_proc_macro_server, prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: config.proc_macro_num_processes(), }; let (db, vfs, _proc_macro) = diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 0dda7f3cc276b..2ccd85f0e34ec 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -948,18 +948,18 @@ config_data! { /// Override the command used for bench runnables. /// The first element of the array should be the program to execute (for example, `cargo`). /// - /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${test_name}` to dynamically + /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically /// replace the package name, target option (such as `--bin` or `--example`), the target name and - /// the test name (name of test function or test mod path). + /// the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). runnables_bench_overrideCommand: Option> = None, /// Command to be executed instead of 'cargo' for runnables. runnables_command: Option = None, /// Override the command used for bench runnables. /// The first element of the array should be the program to execute (for example, `cargo`). /// - /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${test_name}` to dynamically + /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically /// replace the package name, target option (such as `--bin` or `--example`), the target name and - /// the test name (name of test function or test mod path). + /// the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). runnables_doctest_overrideCommand: Option> = None, /// Additional arguments to be passed to cargo for runnables such as /// tests or binaries. For example, it may be `--release`. @@ -977,9 +977,9 @@ config_data! { /// Override the command used for test runnables. /// The first element of the array should be the program to execute (for example, `cargo`). /// - /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${test_name}` to dynamically + /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically /// replace the package name, target option (such as `--bin` or `--example`), the target name and - /// the test name (name of test function or test mod path). + /// the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). runnables_test_overrideCommand: Option> = None, /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index d16ca2fb48ac0..6a74b8a54deb6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -53,6 +53,7 @@ fn integrated_highlighting_benchmark() { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + num_worker_threads: 1, proc_macro_processes: 1, }; @@ -122,6 +123,7 @@ fn integrated_completion_benchmark() { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, + num_worker_threads: 1, proc_macro_processes: 1, }; @@ -324,6 +326,7 @@ fn integrated_diagnostics_benchmark() { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, + num_worker_threads: 1, proc_macro_processes: 1, }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 64decc9e0db60..83edbc722bd57 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -303,6 +303,15 @@ impl GlobalState { .map(Some) } + fn trigger_garbage_collection(&mut self) { + if cfg!(test) { + // Slow tests run the main loop in multiple threads, but GC isn't thread safe. + return; + } + + self.analysis_host.trigger_garbage_collection(); + } + fn handle_event(&mut self, event: Event) { let loop_start = Instant::now(); let _p = tracing::info_span!("GlobalState::handle_event", event = %event).entered(); @@ -383,7 +392,7 @@ impl GlobalState { )); } PrimeCachesProgress::End { cancelled } => { - self.analysis_host.trigger_garbage_collection(); + self.trigger_garbage_collection(); self.prime_caches_queue.op_completed(()); if cancelled { self.prime_caches_queue @@ -542,7 +551,7 @@ impl GlobalState { && self.fmt_pool.handle.is_empty() && current_revision != self.last_gc_revision { - self.analysis_host.trigger_garbage_collection(); + self.trigger_garbage_collection(); self.last_gc_revision = current_revision; } } @@ -1178,6 +1187,8 @@ impl GlobalState { } => self.diagnostics.clear_check_older_than_for_package(id, package_id, generation), FlycheckMessage::Progress { id, progress } => { let format_with_id = |user_facing_command: String| { + // When we're running multiple flychecks, we have to include a disambiguator in + // the title, or the editor complains. Note that this is a user-facing string. if self.flycheck.len() == 1 { user_facing_command } else { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index b8d9acc02a328..8be061cacfa89 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -6,7 +6,7 @@ use cargo_metadata::PackageId; use cfg::{CfgAtom, CfgExpr}; use hir::sym; use ide::{Cancellable, Crate, FileId, RunnableKind, TestId}; -use project_model::project_json::Runnable; +use project_model::project_json::{self, Runnable}; use project_model::{CargoFeatures, ManifestPath, TargetKind}; use rustc_hash::FxHashSet; use triomphe::Arc; @@ -72,48 +72,51 @@ pub(crate) struct ProjectJsonTargetSpec { } impl ProjectJsonTargetSpec { + fn find_replace_runnable( + &self, + kind: project_json::RunnableKind, + replacer: &dyn Fn(&Self, &str) -> String, + ) -> Option { + for runnable in &self.shell_runnables { + if runnable.kind == kind { + let mut runnable = runnable.clone(); + + let replaced_args: Vec<_> = + runnable.args.iter().map(|arg| replacer(self, arg)).collect(); + runnable.args = replaced_args; + + return Some(runnable); + } + } + + None + } + pub(crate) fn runnable_args(&self, kind: &RunnableKind) -> Option { match kind { - RunnableKind::Bin => { - for runnable in &self.shell_runnables { - if matches!(runnable.kind, project_model::project_json::RunnableKind::Run) { - let mut runnable = runnable.clone(); - - let replaced_args: Vec<_> = runnable - .args - .iter() - .map(|arg| arg.replace("{label}", &self.label)) - .collect(); - runnable.args = replaced_args; - - return Some(runnable); - } - } - - None - } + RunnableKind::Bin => self + .find_replace_runnable(project_json::RunnableKind::Run, &|this, arg| { + arg.replace("{label}", &this.label) + }), RunnableKind::Test { test_id, .. } => { - for runnable in &self.shell_runnables { - if matches!(runnable.kind, project_model::project_json::RunnableKind::TestOne) { - let mut runnable = runnable.clone(); - - let replaced_args: Vec<_> = runnable - .args - .iter() - .map(|arg| arg.replace("{test_id}", &test_id.to_string())) - .map(|arg| arg.replace("{label}", &self.label)) - .collect(); - runnable.args = replaced_args; - - return Some(runnable); - } - } - - None + self.find_replace_runnable(project_json::RunnableKind::Run, &|this, arg| { + arg.replace("{label}", &this.label).replace("{test_id}", &test_id.to_string()) + }) + } + RunnableKind::TestMod { path } => self + .find_replace_runnable(project_json::RunnableKind::TestMod, &|this, arg| { + arg.replace("{label}", &this.label).replace("{test_pattern}", path) + }), + RunnableKind::Bench { test_id } => { + self.find_replace_runnable(project_json::RunnableKind::BenchOne, &|this, arg| { + arg.replace("{label}", &this.label).replace("{bench_id}", &test_id.to_string()) + }) + } + RunnableKind::DocTest { test_id } => { + self.find_replace_runnable(project_json::RunnableKind::DocTestOne, &|this, arg| { + arg.replace("{label}", &this.label).replace("{test_id}", &test_id.to_string()) + }) } - RunnableKind::TestMod { .. } => None, - RunnableKind::Bench { .. } => None, - RunnableKind::DocTest { .. } => None, } } } @@ -129,38 +132,21 @@ impl CargoTargetSpec { let extra_test_binary_args = config.extra_test_binary_args; let mut cargo_args = Vec::new(); - let mut executable_args = Vec::new(); + let executable_args = Self::executable_args_for(kind, extra_test_binary_args); match kind { - RunnableKind::Test { test_id, attr } => { + RunnableKind::Test { .. } => { cargo_args.push(config.test_command); - executable_args.push(test_id.to_string()); - if let TestId::Path(_) = test_id { - executable_args.push("--exact".to_owned()); - } - executable_args.extend(extra_test_binary_args); - if attr.ignore { - executable_args.push("--ignored".to_owned()); - } } - RunnableKind::TestMod { path } => { + RunnableKind::TestMod { .. } => { cargo_args.push(config.test_command); - executable_args.push(path.clone()); - executable_args.extend(extra_test_binary_args); } - RunnableKind::Bench { test_id } => { + RunnableKind::Bench { .. } => { cargo_args.push(config.bench_command); - executable_args.push(test_id.to_string()); - if let TestId::Path(_) = test_id { - executable_args.push("--exact".to_owned()); - } - executable_args.extend(extra_test_binary_args); } - RunnableKind::DocTest { test_id } => { + RunnableKind::DocTest { .. } => { cargo_args.push("test".to_owned()); cargo_args.push("--doc".to_owned()); - executable_args.push(test_id.to_string()); - executable_args.extend(extra_test_binary_args); } RunnableKind::Bin => { let subcommand = match spec { @@ -253,16 +239,70 @@ impl CargoTargetSpec { TargetKind::BuildScript | TargetKind::Other => "", }; + let target = |kind, target| match kind { + TargetKind::Bin | TargetKind::Test | TargetKind::Bench | TargetKind::Example => target, + _ => "", + }; + let replace_placeholders = |arg: String| match &spec { Some(spec) => arg .replace("${package}", &spec.package) .replace("${target_arg}", target_arg(spec.target_kind)) - .replace("${target}", &spec.target) + .replace("${target}", target(spec.target_kind, &spec.target)) .replace("${test_name}", &test_name), _ => arg, }; - args.map(|args| args.into_iter().map(replace_placeholders).collect()) + let extra_test_binary_args = config.extra_test_binary_args; + let executable_args = Self::executable_args_for(kind, extra_test_binary_args); + + args.map(|mut args| { + let exec_args_idx = args.iter().position(|a| a == "${executable_args}"); + + if let Some(idx) = exec_args_idx { + args.splice(idx..idx + 1, executable_args); + } + + args.into_iter().map(replace_placeholders).filter(|a| !a.trim().is_empty()).collect() + }) + } + + fn executable_args_for( + kind: &RunnableKind, + extra_test_binary_args: impl IntoIterator, + ) -> Vec { + let mut executable_args = Vec::new(); + + match kind { + RunnableKind::Test { test_id, attr } => { + executable_args.push(test_id.to_string()); + if let TestId::Path(_) = test_id { + executable_args.push("--exact".to_owned()); + } + executable_args.extend(extra_test_binary_args); + if attr.ignore { + executable_args.push("--ignored".to_owned()); + } + } + RunnableKind::TestMod { path } => { + executable_args.push(path.clone()); + executable_args.extend(extra_test_binary_args); + } + RunnableKind::Bench { test_id } => { + executable_args.push(test_id.to_string()); + if let TestId::Path(_) = test_id { + executable_args.push("--exact".to_owned()); + } + executable_args.extend(extra_test_binary_args); + } + RunnableKind::DocTest { test_id } => { + executable_args.push(test_id.to_string()); + executable_args.extend(extra_test_binary_args); + } + RunnableKind::Bin => {} + } + + executable_args } pub(crate) fn push_to(self, buf: &mut Vec, kind: &RunnableKind) { diff --git a/src/tools/rust-analyzer/crates/stdx/src/lib.rs b/src/tools/rust-analyzer/crates/stdx/src/lib.rs index 7ab26b189065f..a1af4cc6be5ce 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/lib.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/lib.rs @@ -221,12 +221,7 @@ pub fn trim_indent(mut text: &str) -> String { if text.starts_with('\n') { text = &text[1..]; } - let indent = text - .lines() - .filter(|it| !it.trim().is_empty()) - .map(|it| it.len() - it.trim_start().len()) - .min() - .unwrap_or(0); + let indent = indent_of(text); text.split_inclusive('\n') .map( |line| { @@ -236,6 +231,25 @@ pub fn trim_indent(mut text: &str) -> String { .collect() } +#[must_use] +fn indent_of(text: &str) -> usize { + text.lines() + .filter(|it| !it.trim().is_empty()) + .map(|it| it.len() - it.trim_start().len()) + .min() + .unwrap_or(0) +} + +#[must_use] +pub fn dedent_by(spaces: usize, text: &str) -> String { + text.split_inclusive('\n') + .map(|line| { + let trimmed = line.trim_start_matches(' '); + if line.len() - trimmed.len() <= spaces { trimmed } else { &line[spaces..] } + }) + .collect() +} + pub fn equal_range_by(slice: &[T], mut key: F) -> ops::Range where F: FnMut(&T) -> Ordering, @@ -366,6 +380,37 @@ mod tests { ); } + #[test] + fn test_dedent() { + assert_eq!(dedent_by(0, ""), ""); + assert_eq!(dedent_by(1, ""), ""); + assert_eq!(dedent_by(2, ""), ""); + assert_eq!(dedent_by(0, "foo"), "foo"); + assert_eq!(dedent_by(2, "foo"), "foo"); + assert_eq!(dedent_by(2, " foo"), "foo"); + assert_eq!(dedent_by(2, " foo"), " foo"); + assert_eq!(dedent_by(2, " foo\nbar"), " foo\nbar"); + assert_eq!(dedent_by(2, "foo\n bar"), "foo\n bar"); + assert_eq!(dedent_by(2, "foo\n\n bar"), "foo\n\n bar"); + assert_eq!(dedent_by(2, "foo\n.\n bar"), "foo\n.\n bar"); + assert_eq!(dedent_by(2, "foo\n .\n bar"), "foo\n.\n bar"); + assert_eq!(dedent_by(2, "foo\n .\n bar"), "foo\n .\n bar"); + } + + #[test] + fn test_indent_of() { + assert_eq!(indent_of(""), 0); + assert_eq!(indent_of(" "), 0); + assert_eq!(indent_of(" x"), 1); + assert_eq!(indent_of(" x\n"), 1); + assert_eq!(indent_of(" x\ny"), 0); + assert_eq!(indent_of(" x\n y"), 1); + assert_eq!(indent_of(" x\n y"), 1); + assert_eq!(indent_of(" x\n y"), 2); + assert_eq!(indent_of(" x\n y\n"), 2); + assert_eq!(indent_of(" x\n\n y\n"), 2); + } + #[test] fn test_replace() { #[track_caller] diff --git a/src/tools/rust-analyzer/crates/syntax/src/algo.rs b/src/tools/rust-analyzer/crates/syntax/src/algo.rs index 3ab9c902625f5..c679921b3f658 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/algo.rs @@ -132,3 +132,19 @@ pub fn previous_non_trivia_token(e: impl Into) -> Option) -> Option { + let mut token = match e.into() { + SyntaxElement::Node(n) => n.last_token()?, + SyntaxElement::Token(t) => t, + } + .next_token(); + while let Some(inner) = token { + if !inner.kind().is_trivia() { + return Some(inner); + } else { + token = inner.next_token(); + } + } + None +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs index 9b30642fe4b08..194d06900a489 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -43,6 +43,12 @@ impl ops::Add for IndentLevel { } } +impl ops::AddAssign for IndentLevel { + fn add_assign(&mut self, rhs: u8) { + self.0 += rhs; + } +} + impl IndentLevel { pub fn single() -> IndentLevel { IndentLevel(0) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 50fe56538080c..55c80ed167077 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -5,7 +5,7 @@ use crate::{ AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, ast::{ self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, - HasTypeBounds, HasVisibility, Param, RangeItem, make, + HasTypeBounds, HasVisibility, Lifetime, Param, RangeItem, make, }, syntax_editor::SyntaxMappingBuilder, }; @@ -21,6 +21,14 @@ impl SyntaxFactory { make::name_ref(name).clone_for_update() } + pub fn name_ref_self_ty(&self) -> ast::NameRef { + make::name_ref_self_ty().clone_for_update() + } + + pub fn expr_todo(&self) -> ast::Expr { + make::ext::expr_todo().clone_for_update() + } + pub fn lifetime(&self, text: &str) -> ast::Lifetime { make::lifetime(text).clone_for_update() } @@ -96,24 +104,47 @@ impl SyntaxFactory { generic_param_list: Option, field_list: ast::FieldList, ) -> ast::Struct { - make::struct_(visibility, strukt_name, generic_param_list, field_list).clone_for_update() - } + let ast = make::struct_( + visibility.clone(), + strukt_name.clone(), + generic_param_list.clone(), + field_list.clone(), + ) + .clone_for_update(); - pub fn enum_( - &self, - attrs: impl IntoIterator, - visibility: Option, - enum_name: ast::Name, - generic_param_list: Option, - where_clause: Option, - variant_list: ast::VariantList, - ) -> ast::Enum { - make::enum_(attrs, visibility, enum_name, generic_param_list, where_clause, variant_list) - .clone_for_update() + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + if let Some(visibility) = visibility { + builder.map_node( + visibility.syntax().clone(), + ast.visibility().unwrap().syntax().clone(), + ); + } + builder.map_node(strukt_name.syntax().clone(), ast.name().unwrap().syntax().clone()); + if let Some(generic_param_list) = generic_param_list { + builder.map_node( + generic_param_list.syntax().clone(), + ast.generic_param_list().unwrap().syntax().clone(), + ); + } + builder + .map_node(field_list.syntax().clone(), ast.field_list().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast } pub fn unnamed_param(&self, ty: ast::Type) -> ast::Param { - make::unnamed_param(ty).clone_for_update() + let ast = make::unnamed_param(ty.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast } pub fn ty_fn_ptr>( @@ -123,7 +154,27 @@ impl SyntaxFactory { params: I, ret_type: Option, ) -> ast::FnPtrType { - make::ty_fn_ptr(is_unsafe, abi, params, ret_type).clone_for_update() + let (params, params_input) = iterator_input(params); + let ast = make::ty_fn_ptr(is_unsafe, abi.clone(), params.into_iter(), ret_type.clone()) + .clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + if let Some(abi) = abi { + builder.map_node(abi.syntax().clone(), ast.abi().unwrap().syntax().clone()); + } + builder.map_children( + params_input, + ast.param_list().unwrap().params().map(|p| p.syntax().clone()), + ); + if let Some(ret_type) = ret_type { + builder + .map_node(ret_type.syntax().clone(), ast.ret_type().unwrap().syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast } pub fn where_pred( @@ -131,18 +182,61 @@ impl SyntaxFactory { path: Either, bounds: impl IntoIterator, ) -> ast::WherePred { - make::where_pred(path, bounds).clone_for_update() + let (bounds, bounds_input) = iterator_input(bounds); + let ast = make::where_pred(path.clone(), bounds).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + match &path { + Either::Left(lifetime) => { + builder.map_node( + lifetime.syntax().clone(), + ast.lifetime().unwrap().syntax().clone(), + ); + } + Either::Right(ty) => { + builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone()); + } + } + if let Some(type_bound_list) = ast.type_bound_list() { + builder.map_children( + bounds_input, + type_bound_list.bounds().map(|b| b.syntax().clone()), + ); + } + builder.finish(&mut mapping); + } + + ast } pub fn where_clause( &self, predicates: impl IntoIterator, ) -> ast::WhereClause { - make::where_clause(predicates).clone_for_update() + let (predicates, input) = iterator_input(predicates); + let ast = make::where_clause(predicates).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input, ast.predicates().map(|p| p.syntax().clone())); + builder.finish(&mut mapping); + } + + ast } pub fn impl_trait_type(&self, bounds: ast::TypeBoundList) -> ast::ImplTraitType { - make::impl_trait_type(bounds).clone_for_update() + let ast = make::impl_trait_type(bounds.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder + .map_node(bounds.syntax().clone(), ast.type_bound_list().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast } pub fn expr_field(&self, receiver: ast::Expr, field: &str) -> ast::FieldExpr { @@ -340,15 +434,53 @@ impl SyntaxFactory { name_ref: ast::NameRef, generic_args: impl IntoIterator, ) -> ast::PathSegment { - make::generic_ty_path_segment(name_ref, generic_args).clone_for_update() + let (generic_args, input) = iterator_input(generic_args); + let ast = make::generic_ty_path_segment(name_ref.clone(), generic_args).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone()); + builder.map_children( + input, + ast.generic_arg_list().unwrap().generic_args().map(|a| a.syntax().clone()), + ); + builder.finish(&mut mapping); + } + + ast } pub fn tail_only_block_expr(&self, tail_expr: ast::Expr) -> ast::BlockExpr { - make::tail_only_block_expr(tail_expr) + let ast = make::tail_only_block_expr(tail_expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let stmt_list = ast.stmt_list().unwrap(); + let mut builder = SyntaxMappingBuilder::new(stmt_list.syntax().clone()); + builder.map_node( + tail_expr.syntax().clone(), + stmt_list.tail_expr().unwrap().syntax().clone(), + ); + builder.finish(&mut mapping); + } + + ast } pub fn expr_bin_op(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::Expr { - make::expr_bin_op(lhs, op, rhs) + let ast::Expr::BinExpr(ast) = + make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update() + else { + unreachable!() + }; + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(lhs.syntax().clone(), ast.lhs().unwrap().syntax().clone()); + builder.map_node(rhs.syntax().clone(), ast.rhs().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast.into() } pub fn ty_placeholder(&self) -> ast::Type { @@ -385,7 +517,23 @@ impl SyntaxFactory { visibility: Option, use_tree: ast::UseTree, ) -> ast::Use { - make::use_(attrs, visibility, use_tree).clone_for_update() + let (attrs, attrs_input) = iterator_input(attrs); + let ast = make::use_(attrs, visibility.clone(), use_tree.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(attrs_input, ast.attrs().map(|attr| attr.syntax().clone())); + if let Some(visibility) = visibility { + builder.map_node( + visibility.syntax().clone(), + ast.visibility().unwrap().syntax().clone(), + ); + } + builder.map_node(use_tree.syntax().clone(), ast.use_tree().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast } pub fn use_tree( @@ -395,7 +543,25 @@ impl SyntaxFactory { alias: Option, add_star: bool, ) -> ast::UseTree { - make::use_tree(path, use_tree_list, alias, add_star).clone_for_update() + let ast = make::use_tree(path.clone(), use_tree_list.clone(), alias.clone(), add_star) + .clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone()); + if let Some(use_tree_list) = use_tree_list { + builder.map_node( + use_tree_list.syntax().clone(), + ast.use_tree_list().unwrap().syntax().clone(), + ); + } + if let Some(alias) = alias { + builder.map_node(alias.syntax().clone(), ast.rename().unwrap().syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast } pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path { @@ -896,10 +1062,6 @@ impl SyntaxFactory { unreachable!() }; - if let Some(mut mapping) = self.mappings() { - SyntaxMappingBuilder::new(ast.syntax().clone()).finish(&mut mapping); - } - ast } @@ -1765,6 +1927,65 @@ impl SyntaxFactory { } ast } + + pub fn field_from_idents<'a>( + &self, + parts: impl std::iter::IntoIterator, + ) -> Option { + make::ext::field_from_idents(parts) + } + + pub fn expr_await(&self, expr: ast::Expr) -> ast::AwaitExpr { + let ast::Expr::AwaitExpr(ast) = make::expr_await(expr.clone()).clone_for_update() else { + unreachable!() + }; + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn expr_break(&self, label: Option, expr: Option) -> ast::BreakExpr { + let ast::Expr::BreakExpr(ast) = + make::expr_break(label.clone(), expr.clone()).clone_for_update() + else { + unreachable!() + }; + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + if let Some(label) = label { + builder.map_node(label.syntax().clone(), ast.lifetime().unwrap().syntax().clone()); + } + if let Some(expr) = expr { + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast + } + + pub fn expr_continue(&self, label: Option) -> ast::ContinueExpr { + let ast::Expr::ContinueExpr(ast) = make::expr_continue(label.clone()).clone_for_update() + else { + unreachable!() + }; + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + if let Some(label) = label { + builder.map_node(label.syntax().clone(), ast.lifetime().unwrap().syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast + } } // `ext` constructors diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 8460c2c7d0cf2..35fba5accdbbe 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1380,9 +1380,9 @@ Default: `null` Override the command used for bench runnables. The first element of the array should be the program to execute (for example, `cargo`). -Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${test_name}` to dynamically +Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically replace the package name, target option (such as `--bin` or `--example`), the target name and -the test name (name of test function or test mod path). +the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). ## rust-analyzer.runnables.command {#runnables.command} @@ -1399,9 +1399,9 @@ Default: `null` Override the command used for bench runnables. The first element of the array should be the program to execute (for example, `cargo`). -Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${test_name}` to dynamically +Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically replace the package name, target option (such as `--bin` or `--example`), the target name and -the test name (name of test function or test mod path). +the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). ## rust-analyzer.runnables.extraArgs {#runnables.extraArgs} @@ -1444,9 +1444,9 @@ Default: `null` Override the command used for test runnables. The first element of the array should be the program to execute (for example, `cargo`). -Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${test_name}` to dynamically +Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically replace the package name, target option (such as `--bin` or `--example`), the target name and -the test name (name of test function or test mod path). +the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). ## rust-analyzer.rustc.source {#rustc.source} diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index be0bdc8d55bbf..047dbba11fcfc 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -3697,9 +3697,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -6719,9 +6719,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "6.21.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", - "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", + "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", "dev": true, "license": "MIT", "engines": { diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index fc20597e88764..1dd513c9de404 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2865,7 +2865,7 @@ "title": "Runnables", "properties": { "rust-analyzer.runnables.bench.overrideCommand": { - "markdownDescription": "Override the command used for bench runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${test_name}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe test name (name of test function or test mod path).", + "markdownDescription": "Override the command used for bench runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).", "default": null, "type": [ "null", @@ -2894,7 +2894,7 @@ "title": "Runnables", "properties": { "rust-analyzer.runnables.doctest.overrideCommand": { - "markdownDescription": "Override the command used for bench runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${test_name}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe test name (name of test function or test mod path).", + "markdownDescription": "Override the command used for bench runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).", "default": null, "type": [ "null", @@ -2948,7 +2948,7 @@ "title": "Runnables", "properties": { "rust-analyzer.runnables.test.overrideCommand": { - "markdownDescription": "Override the command used for test runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${test_name}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe test name (name of test function or test mod path).", + "markdownDescription": "Override the command used for test runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).", "default": null, "type": [ "null", diff --git a/tests/assembly-llvm/bpf_unaligned.rs b/tests/assembly-llvm/bpf_unaligned.rs new file mode 100644 index 0000000000000..466c3e411ec2c --- /dev/null +++ b/tests/assembly-llvm/bpf_unaligned.rs @@ -0,0 +1,27 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ compile-flags: --target bpfel-unknown-none -C target_feature=+allows-misaligned-mem-access +//@ min-llvm-version: 22 +//@ needs-llvm-components: bpf +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +// CHECK-LABEL: test_load_i64: +// CHECK: r0 = *(u64 *)(r1 + 0) +#[no_mangle] +pub unsafe fn test_load_i64(p: *const u64) -> u64 { + let mut tmp: u64 = 0; + copy_nonoverlapping(p as *const u8, &mut tmp as *mut u64 as *mut u8, 8); + tmp +} + +// CHECK-LABEL: test_store_i64: +// CHECK: *(u64 *)(r1 + 0) = r2 +#[no_mangle] +pub unsafe fn test_store_i64(p: *mut u64, v: u64) { + copy_nonoverlapping(&v as *const u64 as *const u8, p as *mut u8, 8); +} diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index c91c5dfbeb157..3b43a0de9db0e 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -277,6 +277,10 @@ trait Drop { fn drop(&mut self); } +#[rustc_nounwind] +#[rustc_intrinsic] +pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); + pub mod mem { #[rustc_nounwind] #[rustc_intrinsic] diff --git a/tests/codegen-llvm/bpf-allows-unaligned.rs b/tests/codegen-llvm/bpf-allows-unaligned.rs new file mode 100644 index 0000000000000..c7a70d5b2e502 --- /dev/null +++ b/tests/codegen-llvm/bpf-allows-unaligned.rs @@ -0,0 +1,11 @@ +//@ only-bpf +#![crate_type = "lib"] +#![feature(bpf_target_feature)] +#![no_std] + +#[no_mangle] +#[target_feature(enable = "allows-misaligned-mem-access")] +// CHECK: define noundef zeroext i8 @foo(i8 noundef returned %arg) unnamed_addr #0 { +pub unsafe fn foo(arg: u8) -> u8 { + arg +} diff --git a/tests/codegen-llvm/fromrangeiter-overflow-checks.rs b/tests/codegen-llvm/fromrangeiter-overflow-checks.rs index 4d27f118ddd37..455c81c633407 100644 --- a/tests/codegen-llvm/fromrangeiter-overflow-checks.rs +++ b/tests/codegen-llvm/fromrangeiter-overflow-checks.rs @@ -2,7 +2,7 @@ // runtime check that panics after yielding the maximum value of the range bound type. That is // tested for by tests/ui/iterators/rangefrom-overflow-overflow-checks.rs // -// This test ensures that such a runtime check is *not* emitted when debug-assertions are +// This test ensures such runtime checks are optimized out when debug-assertions are // enabled, but overflow-checks are explicitly disabled. //@ revisions: DEBUG NOCHECKS @@ -11,17 +11,24 @@ #![crate_type = "lib"] #![feature(new_range_api)] -use std::range::{RangeFrom, RangeFromIter}; +use std::range::RangeFrom; -// CHECK-LABEL: @iterrangefrom_remainder( +// CHECK-LABEL: @rangefrom_increments( #[no_mangle] -pub unsafe fn iterrangefrom_remainder(x: RangeFromIter) -> RangeFrom { - // DEBUG: i32 noundef %x - // NOCHECKS: i32 noundef returned %x - // DEBUG: br i1 - // DEBUG: call core::panicking::panic_const::panic_const_add_overflow - // DEBUG: unreachable - // NOCHECKS-NOT: unreachable - // NOCHECKS: ret i32 %x - x.remainder() +pub unsafe fn rangefrom_increments(range: RangeFrom) -> RangeFrom { + // Iterator is contained entirely within this function, so the optimizer should + // be able to see that `exhausted` is never set and optimize out any branches. + + // CHECK: i32 noundef %range + // DEBUG: switch i32 %range + // DEBUG: call core::panicking::panic_const::panic_const_add_overflow + // DEBUG: unreachable + // NOCHECKS-NOT: unreachable + // NOCHECKS: [[REM:%[a-z_0-9.]+]] = add i32 %range, 2 + // NOCHECKS-NEXT: ret i32 [[REM]] + + let mut iter = range.into_iter(); + let _ = iter.next(); + let _ = iter.next(); + iter.remainder() } diff --git a/tests/pretty/or-pattern-paren.pp b/tests/pretty/or-pattern-paren.pp new file mode 100644 index 0000000000000..6ea94eb7b91f8 --- /dev/null +++ b/tests/pretty/or-pattern-paren.pp @@ -0,0 +1,27 @@ +#![feature(prelude_import)] +#![no_std] +#![feature(box_patterns)] +extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; + +//@ pretty-compare-only +//@ pretty-mode:expanded +//@ pp-exact:or-pattern-paren.pp + +macro_rules! or_pat { ($($name:pat),+) => { $($name)|+ } } + +fn check_at(x: Option) { + match x { + Some(v @ (1 | 2 | 3)) => + + + { + ::std::io::_print(format_args!("{0}\n", v)); + } + _ => {} + } +} +fn check_ref(x: &i32) { match x { &(1 | 2 | 3) => {} _ => {} } } +fn check_box(x: Box) { match x { box (1 | 2 | 3) => {} _ => {} } } +fn main() { check_at(Some(2)); check_ref(&1); check_box(Box::new(1)); } diff --git a/tests/pretty/or-pattern-paren.rs b/tests/pretty/or-pattern-paren.rs new file mode 100644 index 0000000000000..ea6a8f3de9e49 --- /dev/null +++ b/tests/pretty/or-pattern-paren.rs @@ -0,0 +1,36 @@ +#![feature(box_patterns)] + +//@ pretty-compare-only +//@ pretty-mode:expanded +//@ pp-exact:or-pattern-paren.pp + +macro_rules! or_pat { + ($($name:pat),+) => { $($name)|+ } +} + +fn check_at(x: Option) { + match x { + Some(v @ or_pat!(1, 2, 3)) => println!("{v}"), + _ => {} + } +} + +fn check_ref(x: &i32) { + match x { + &or_pat!(1, 2, 3) => {} + _ => {} + } +} + +fn check_box(x: Box) { + match x { + box or_pat!(1, 2, 3) => {} + _ => {} + } +} + +fn main() { + check_at(Some(2)); + check_ref(&1); + check_box(Box::new(1)); +} diff --git a/tests/run-make/crate-loading/multiple-dep-versions.stderr b/tests/run-make/crate-loading/multiple-dep-versions.stderr index ef7fb70822665..a683fdd389c6b 100644 --- a/tests/run-make/crate-loading/multiple-dep-versions.stderr +++ b/tests/run-make/crate-loading/multiple-dep-versions.stderr @@ -177,14 +177,14 @@ LL | Err(Error2)?; | this can't be annotated with `?` because it has type `Result<_, Error2>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait -help: the following other types implement trait `From` +help: `dependency::OtherError` implements trait `From` --> replaced | LL | impl From<()> for OtherError { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dependency::OtherError` implements `From<()>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `From<()>` ... LL | impl From for OtherError { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dependency::OtherError` implements `From` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `From` = note: there are multiple different versions of crate `dependency` in the dependency graph = help: you can use `cargo tree` to explore your dependency tree diff --git a/tests/ui/asm/aarch64/bad-options.stderr b/tests/ui/asm/aarch64/bad-options.stderr index 54ab7cafa49f7..f7252d0ad374d 100644 --- a/tests/ui/asm/aarch64/bad-options.stderr +++ b/tests/ui/asm/aarch64/bad-options.stderr @@ -78,7 +78,7 @@ error: invalid ABI for `clobber_abi` LL | asm!("", clobber_abi("foo")); | ^^^^^^^^^^^^^^^^^^ | - = note: the following ABIs are supported on this target: `C`, `system`, `efiapi` + = note: the following ABIs are supported on this target: `C`, `system`, and `efiapi` error: aborting due to 13 previous errors diff --git a/tests/ui/asm/aarch64/bad-reg.rs b/tests/ui/asm/aarch64/bad-reg.rs index b99e5fe4b9e33..676d736e15d7a 100644 --- a/tests/ui/asm/aarch64/bad-reg.rs +++ b/tests/ui/asm/aarch64/bad-reg.rs @@ -1,7 +1,12 @@ +//@ add-minicore //@ only-aarch64 //@ compile-flags: -C target-feature=+neon +#![crate_type = "lib"] +#![feature(no_core)] +#![no_core] -use std::arch::asm; +extern crate minicore; +use minicore::*; fn main() { let mut foo = 0; @@ -14,11 +19,11 @@ fn main() { asm!("", in("foo") foo); //~^ ERROR invalid register `foo`: unknown register asm!("{:z}", in(reg) foo); - //~^ ERROR invalid asm template modifier for this register class + //~^ ERROR invalid asm template modifier `z` for this register class asm!("{:r}", in(vreg) foo); - //~^ ERROR invalid asm template modifier for this register class + //~^ ERROR invalid asm template modifier `r` for this register class asm!("{:r}", in(vreg_low16) foo); - //~^ ERROR invalid asm template modifier for this register class + //~^ ERROR invalid asm template modifier `r` for this register class asm!("{:a}", const 0); //~^ ERROR asm template modifiers are not allowed for `const` arguments asm!("{:a}", sym main); diff --git a/tests/ui/asm/aarch64/bad-reg.stderr b/tests/ui/asm/aarch64/bad-reg.stderr index c76722f32a74e..62f66cf3424f9 100644 --- a/tests/ui/asm/aarch64/bad-reg.stderr +++ b/tests/ui/asm/aarch64/bad-reg.stderr @@ -1,49 +1,49 @@ error: invalid register class `foo`: unknown register class - --> $DIR/bad-reg.rs:12:20 + --> $DIR/bad-reg.rs:17:20 | LL | asm!("{}", in(foo) foo); | ^^^^^^^^^^^ | - = note: the following register classes are supported on this target: `reg`, `vreg`, `vreg_low16`, `preg` + = note: the following register classes are supported on this target: `reg`, `vreg`, `vreg_low16`, and `preg` error: invalid register `foo`: unknown register - --> $DIR/bad-reg.rs:14:18 + --> $DIR/bad-reg.rs:19:18 | LL | asm!("", in("foo") foo); | ^^^^^^^^^^^^^ -error: invalid asm template modifier for this register class - --> $DIR/bad-reg.rs:16:15 +error: invalid asm template modifier `z` for this register class + --> $DIR/bad-reg.rs:21:15 | LL | asm!("{:z}", in(reg) foo); | ^^^^ ----------- argument | | | template modifier | - = note: the `reg` register class supports the following template modifiers: `w`, `x` + = note: the `reg` register class supports the following template modifiers: `w` and `x` -error: invalid asm template modifier for this register class - --> $DIR/bad-reg.rs:18:15 +error: invalid asm template modifier `r` for this register class + --> $DIR/bad-reg.rs:23:15 | LL | asm!("{:r}", in(vreg) foo); | ^^^^ ------------ argument | | | template modifier | - = note: the `vreg` register class supports the following template modifiers: `b`, `h`, `s`, `d`, `q`, `v` + = note: the `vreg` register class supports the following template modifiers: `b`, `h`, `s`, `d`, `q`, and `v` -error: invalid asm template modifier for this register class - --> $DIR/bad-reg.rs:20:15 +error: invalid asm template modifier `r` for this register class + --> $DIR/bad-reg.rs:25:15 | LL | asm!("{:r}", in(vreg_low16) foo); | ^^^^ ------------------ argument | | | template modifier | - = note: the `vreg_low16` register class supports the following template modifiers: `b`, `h`, `s`, `d`, `q`, `v` + = note: the `vreg_low16` register class supports the following template modifiers: `b`, `h`, `s`, `d`, `q`, and `v` error: asm template modifiers are not allowed for `const` arguments - --> $DIR/bad-reg.rs:22:15 + --> $DIR/bad-reg.rs:27:15 | LL | asm!("{:a}", const 0); | ^^^^ ------- argument @@ -51,7 +51,7 @@ LL | asm!("{:a}", const 0); | template modifier error: asm template modifiers are not allowed for `sym` arguments - --> $DIR/bad-reg.rs:24:15 + --> $DIR/bad-reg.rs:29:15 | LL | asm!("{:a}", sym main); | ^^^^ -------- argument @@ -59,49 +59,49 @@ LL | asm!("{:a}", sym main); | template modifier error: invalid register `x29`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", in("x29") foo); | ^^^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", in("sp") foo); | ^^^^^^^^^^^^ error: invalid register `xzr`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", in("xzr") foo); | ^^^^^^^^^^^^^ error: invalid register `x19`: x19 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", in("x19") foo); | ^^^^^^^^^^^^^ error: register class `preg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:40:18 | LL | asm!("", in("p0") foo); | ^^^^^^^^^^^^ error: register class `preg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:39:20 + --> $DIR/bad-reg.rs:44:20 | LL | asm!("{}", in(preg) foo); | ^^^^^^^^^^^^ error: register class `preg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:42:20 + --> $DIR/bad-reg.rs:47:20 | LL | asm!("{}", out(preg) _); | ^^^^^^^^^^^ error: register `w0` conflicts with register `x0` - --> $DIR/bad-reg.rs:48:32 + --> $DIR/bad-reg.rs:53:32 | LL | asm!("", in("x0") foo, in("w0") bar); | ------------ ^^^^^^^^^^^^ register `w0` @@ -109,7 +109,7 @@ LL | asm!("", in("x0") foo, in("w0") bar); | register `x0` error: register `x0` conflicts with register `x0` - --> $DIR/bad-reg.rs:50:32 + --> $DIR/bad-reg.rs:55:32 | LL | asm!("", in("x0") foo, out("x0") bar); | ------------ ^^^^^^^^^^^^^ register `x0` @@ -117,13 +117,13 @@ LL | asm!("", in("x0") foo, out("x0") bar); | register `x0` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:55:18 | LL | asm!("", in("x0") foo, out("x0") bar); | ^^^^^^^^^^^^ error: register `q0` conflicts with register `v0` - --> $DIR/bad-reg.rs:53:32 + --> $DIR/bad-reg.rs:58:32 | LL | asm!("", in("v0") foo, in("q0") bar); | ------------ ^^^^^^^^^^^^ register `q0` @@ -131,7 +131,7 @@ LL | asm!("", in("v0") foo, in("q0") bar); | register `v0` error: register `q0` conflicts with register `v0` - --> $DIR/bad-reg.rs:55:32 + --> $DIR/bad-reg.rs:60:32 | LL | asm!("", in("v0") foo, out("q0") bar); | ------------ ^^^^^^^^^^^^^ register `q0` @@ -139,13 +139,13 @@ LL | asm!("", in("v0") foo, out("q0") bar); | register `v0` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:55:18 + --> $DIR/bad-reg.rs:60:18 | LL | asm!("", in("v0") foo, out("q0") bar); | ^^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:35:27 + --> $DIR/bad-reg.rs:40:27 | LL | asm!("", in("p0") foo); | ^^^ @@ -153,7 +153,7 @@ LL | asm!("", in("p0") foo); = note: register class `preg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:39:29 + --> $DIR/bad-reg.rs:44:29 | LL | asm!("{}", in(preg) foo); | ^^^ diff --git a/tests/ui/asm/x86_64/bad-clobber-abi.stderr b/tests/ui/asm/x86_64/bad-clobber-abi.stderr index 46e91a3951fb5..ec0d85f727fc7 100644 --- a/tests/ui/asm/x86_64/bad-clobber-abi.stderr +++ b/tests/ui/asm/x86_64/bad-clobber-abi.stderr @@ -4,7 +4,7 @@ error: invalid ABI for `clobber_abi` LL | asm!("", clobber_abi("foo")); | ^^^^^^^^^^^^^^^^^^ | - = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64` + = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, and `sysv64` error: invalid ABI for `clobber_abi` --> $DIR/bad-clobber-abi.rs:13:35 @@ -12,7 +12,7 @@ error: invalid ABI for `clobber_abi` LL | asm!("", clobber_abi("C", "foo")); | ^^^^^ | - = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64` + = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, and `sysv64` error: `C` ABI specified multiple times --> $DIR/bad-clobber-abi.rs:15:35 @@ -38,7 +38,7 @@ error: invalid ABI for `clobber_abi` LL | asm!("", clobber_abi("C", "foo", "C")); | ^^^^^ | - = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64` + = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, and `sysv64` error: `C` ABI specified multiple times --> $DIR/bad-clobber-abi.rs:20:42 @@ -54,7 +54,7 @@ error: invalid ABI for `clobber_abi` LL | asm!("", clobber_abi("win64", "foo", "efiapi")); | ^^^^^ | - = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64` + = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, and `sysv64` error: `win64` ABI specified multiple times --> $DIR/bad-clobber-abi.rs:23:46 diff --git a/tests/ui/asm/x86_64/bad-options.stderr b/tests/ui/asm/x86_64/bad-options.stderr index 366eb7cb90f39..a09f02e68ea96 100644 --- a/tests/ui/asm/x86_64/bad-options.stderr +++ b/tests/ui/asm/x86_64/bad-options.stderr @@ -93,7 +93,7 @@ error: invalid ABI for `clobber_abi` LL | asm!("", clobber_abi("foo")); | ^^^^^^^^^^^^^^^^^^ | - = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64` + = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, and `sysv64` error: `C` ABI specified multiple times --> $DIR/bad-options.rs:28:52 diff --git a/tests/ui/asm/x86_64/bad-reg.experimental_reg.stderr b/tests/ui/asm/x86_64/bad-reg.experimental_reg.stderr index 133921b894dd0..fe2a53aec5d48 100644 --- a/tests/ui/asm/x86_64/bad-reg.experimental_reg.stderr +++ b/tests/ui/asm/x86_64/bad-reg.experimental_reg.stderr @@ -4,7 +4,7 @@ error: invalid register class `foo`: unknown register class LL | asm!("{}", in(foo) foo); | ^^^^^^^^^^^ | - = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, `tmm_reg` + = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, and `tmm_reg` error: invalid register `foo`: unknown register --> $DIR/bad-reg.rs:22:18 @@ -12,7 +12,7 @@ error: invalid register `foo`: unknown register LL | asm!("", in("foo") foo); | ^^^^^^^^^^^^^ -error: invalid asm template modifier for this register class +error: invalid asm template modifier `z` for this register class --> $DIR/bad-reg.rs:24:15 | LL | asm!("{:z}", in(reg) foo); @@ -20,9 +20,9 @@ LL | asm!("{:z}", in(reg) foo); | | | template modifier | - = note: the `reg` register class supports the following template modifiers: `l`, `x`, `e`, `r` + = note: the `reg` register class supports the following template modifiers: `l`, `x`, `e`, and `r` -error: invalid asm template modifier for this register class +error: invalid asm template modifier `r` for this register class --> $DIR/bad-reg.rs:26:15 | LL | asm!("{:r}", in(xmm_reg) foo); @@ -30,7 +30,7 @@ LL | asm!("{:r}", in(xmm_reg) foo); | | | template modifier | - = note: the `xmm_reg` register class supports the following template modifiers: `x`, `y`, `z` + = note: the `xmm_reg` register class supports the following template modifiers: `x`, `y`, and `z` error: asm template modifiers are not allowed for `const` arguments --> $DIR/bad-reg.rs:28:15 diff --git a/tests/ui/asm/x86_64/bad-reg.rs b/tests/ui/asm/x86_64/bad-reg.rs index 4b3005d5675b3..d5b5cd39bf776 100644 --- a/tests/ui/asm/x86_64/bad-reg.rs +++ b/tests/ui/asm/x86_64/bad-reg.rs @@ -22,9 +22,9 @@ fn main() { asm!("", in("foo") foo); //~^ ERROR invalid register `foo`: unknown register asm!("{:z}", in(reg) foo); - //~^ ERROR invalid asm template modifier for this register class + //~^ ERROR invalid asm template modifier `z` for this register class asm!("{:r}", in(xmm_reg) foo); - //~^ ERROR invalid asm template modifier for this register class + //~^ ERROR invalid asm template modifier `r` for this register class asm!("{:a}", const 0); //~^ ERROR asm template modifiers are not allowed for `const` arguments asm!("{:a}", sym main); diff --git a/tests/ui/asm/x86_64/bad-reg.stable.stderr b/tests/ui/asm/x86_64/bad-reg.stable.stderr index a1c2792a5b1fc..d8a37933065e1 100644 --- a/tests/ui/asm/x86_64/bad-reg.stable.stderr +++ b/tests/ui/asm/x86_64/bad-reg.stable.stderr @@ -4,7 +4,7 @@ error: invalid register class `foo`: unknown register class LL | asm!("{}", in(foo) foo); | ^^^^^^^^^^^ | - = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, `tmm_reg` + = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, and `tmm_reg` error: invalid register `foo`: unknown register --> $DIR/bad-reg.rs:22:18 @@ -12,7 +12,7 @@ error: invalid register `foo`: unknown register LL | asm!("", in("foo") foo); | ^^^^^^^^^^^^^ -error: invalid asm template modifier for this register class +error: invalid asm template modifier `z` for this register class --> $DIR/bad-reg.rs:24:15 | LL | asm!("{:z}", in(reg) foo); @@ -20,9 +20,9 @@ LL | asm!("{:z}", in(reg) foo); | | | template modifier | - = note: the `reg` register class supports the following template modifiers: `l`, `x`, `e`, `r` + = note: the `reg` register class supports the following template modifiers: `l`, `x`, `e`, and `r` -error: invalid asm template modifier for this register class +error: invalid asm template modifier `r` for this register class --> $DIR/bad-reg.rs:26:15 | LL | asm!("{:r}", in(xmm_reg) foo); @@ -30,7 +30,7 @@ LL | asm!("{:r}", in(xmm_reg) foo); | | | template modifier | - = note: the `xmm_reg` register class supports the following template modifiers: `x`, `y`, `z` + = note: the `xmm_reg` register class supports the following template modifiers: `x`, `y`, and `z` error: asm template modifiers are not allowed for `const` arguments --> $DIR/bad-reg.rs:28:15 diff --git a/tests/ui/asm/x86_64/issue-82869.stderr b/tests/ui/asm/x86_64/issue-82869.stderr index 56e4909956913..5cb7e6e27efaa 100644 --- a/tests/ui/asm/x86_64/issue-82869.stderr +++ b/tests/ui/asm/x86_64/issue-82869.stderr @@ -4,7 +4,7 @@ error: invalid register class `vreg`: unknown register class LL | asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") { | ^^^^^^^^^^^ | - = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, `tmm_reg` + = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, and `tmm_reg` error: invalid register class `vreg`: unknown register class --> $DIR/issue-82869.rs:11:45 @@ -12,7 +12,7 @@ error: invalid register class `vreg`: unknown register class LL | asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") { | ^^^^^^^^^^ | - = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, `tmm_reg` + = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, and `tmm_reg` error: invalid register `d0`: unknown register --> $DIR/issue-82869.rs:11:57 diff --git a/tests/ui/attributes/malformed-reprs.stderr b/tests/ui/attributes/malformed-reprs.stderr index 504ba91aac5f5..6e77c6e4b9a97 100644 --- a/tests/ui/attributes/malformed-reprs.stderr +++ b/tests/ui/attributes/malformed-reprs.stderr @@ -6,12 +6,6 @@ LL | #![repr] | = note: for more information, visit -error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/malformed-reprs.rs:9:14 - | -LL | #[repr(align(0))] - | ^ - error: `repr` attribute cannot be used at crate level --> $DIR/malformed-reprs.rs:4:1 | @@ -19,7 +13,7 @@ LL | #![repr] | ^^^^^^^^ ... LL | enum Foo {} - | --- the inner attribute doesn't annotate this enum + | ----------- the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | @@ -27,6 +21,12 @@ LL - #![repr] LL + #[repr] | +error[E0589]: invalid `repr(align)` attribute: not a power of two + --> $DIR/malformed-reprs.rs:9:14 + | +LL | #[repr(align(0))] + | ^ + error[E0084]: unsupported representation for zero-variant enum --> $DIR/malformed-reprs.rs:9:1 | diff --git a/tests/ui/attributes/rustc_confusables_assoc_fn.rs b/tests/ui/attributes/rustc_confusables_assoc_fn.rs new file mode 100644 index 0000000000000..5612ece0a8d95 --- /dev/null +++ b/tests/ui/attributes/rustc_confusables_assoc_fn.rs @@ -0,0 +1,22 @@ +#![feature(rustc_attrs)] + +struct S; + +impl S { + #[rustc_confusables("bar")] + fn foo() {} + + #[rustc_confusables("baz")] + fn qux(&self, x: i32) {} +} + +fn main() { + S::bar(); + //~^ ERROR no function or associated item named `bar` + //~| HELP you might have meant to use `foo` + + let s = S; + s.baz(10); + //~^ ERROR no method named `baz` + //~| HELP you might have meant to use `qux` +} diff --git a/tests/ui/attributes/rustc_confusables_assoc_fn.stderr b/tests/ui/attributes/rustc_confusables_assoc_fn.stderr new file mode 100644 index 0000000000000..657ca8098b802 --- /dev/null +++ b/tests/ui/attributes/rustc_confusables_assoc_fn.stderr @@ -0,0 +1,33 @@ +error[E0599]: no function or associated item named `bar` found for struct `S` in the current scope + --> $DIR/rustc_confusables_assoc_fn.rs:14:8 + | +LL | struct S; + | -------- function or associated item `bar` not found for this struct +... +LL | S::bar(); + | ^^^ function or associated item not found in `S` + | +help: you might have meant to use `foo` + | +LL - S::bar(); +LL + S::foo(); + | + +error[E0599]: no method named `baz` found for struct `S` in the current scope + --> $DIR/rustc_confusables_assoc_fn.rs:19:7 + | +LL | struct S; + | -------- method `baz` not found for this struct +... +LL | s.baz(10); + | ^^^ method not found in `S` + | +help: you might have meant to use `qux` + | +LL - s.baz(10); +LL + s.qux(10); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 06a7f477a7fdf..06384c2202f1e 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -17,6 +17,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `addsubiw` `adx` `aes` +`allows-misaligned-mem-access` `altivec` `alu32` `amx-avx512` diff --git a/tests/ui/const-generics/exhaustive-value.stderr b/tests/ui/const-generics/exhaustive-value.stderr index 791bb19ffe866..3377f4cc4e11c 100644 --- a/tests/ui/const-generics/exhaustive-value.stderr +++ b/tests/ui/const-generics/exhaustive-value.stderr @@ -4,15 +4,15 @@ error[E0277]: the trait bound `(): Foo` is not satisfied LL | <() as Foo>::test() | ^^ the trait `Foo` is not implemented for `()` | - = help: the following other types implement trait `Foo`: - `()` implements `Foo<0>` - `()` implements `Foo<100>` - `()` implements `Foo<101>` - `()` implements `Foo<102>` - `()` implements `Foo<103>` - `()` implements `Foo<104>` - `()` implements `Foo<105>` - `()` implements `Foo<106>` + = help: `()` implements trait `Foo`: + Foo<0> + Foo<100> + Foo<101> + Foo<102> + Foo<103> + Foo<104> + Foo<105> + Foo<106> and 248 others error: aborting due to 1 previous error diff --git a/tests/ui/derives/issue-36617.stderr b/tests/ui/derives/issue-36617.stderr index 3de1d87c5046c..f76c5dfc7e067 100644 --- a/tests/ui/derives/issue-36617.stderr +++ b/tests/ui/derives/issue-36617.stderr @@ -5,7 +5,7 @@ LL | #![derive(Copy)] | ^^^^^^^^^^^^^^^^ ... LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | @@ -20,7 +20,7 @@ LL | #![test] | ^^^^^^^^ ... LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | @@ -35,7 +35,7 @@ LL | #![test_case] | ^^^^^^^^^^^^^ ... LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | @@ -50,7 +50,7 @@ LL | #![bench] | ^^^^^^^^^ ... LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | @@ -65,7 +65,7 @@ LL | #![global_allocator] | ^^^^^^^^^^^^^^^^^^^^ ... LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr b/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr index 4a38546e2a88f..0c07e5afb6712 100644 --- a/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr +++ b/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr @@ -11,14 +11,14 @@ help: the trait `Foo` is not implemented for `Bar` | LL | struct Bar; | ^^^^^^^^^^ -help: the following other types implement trait `Foo` +help: `Bar` implements trait `Foo` --> $DIR/issue-21659-show-relevant-trait-impls-1.rs:15:1 | LL | impl Foo for Bar {} - | ^^^^^^^^^^^^^^^^^^^^^ `Bar` implements `Foo` + | ^^^^^^^^^^^^^^^^^^^^^ `Foo` LL | LL | impl Foo for Bar {} - | ^^^^^^^^^^^^^^^^^^^^ `Bar` implements `Foo` + | ^^^^^^^^^^^^^^^^^^^^ `Foo` error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr b/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr index 8d1c05e7b544c..cb4ca9082b44c 100644 --- a/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr +++ b/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr @@ -11,13 +11,13 @@ help: the trait `Foo` is not implemented for `Bar` | LL | struct Bar; | ^^^^^^^^^^ - = help: the following other types implement trait `Foo`: - `Bar` implements `Foo` - `Bar` implements `Foo` - `Bar` implements `Foo` - `Bar` implements `Foo` - `Bar` implements `Foo` - `Bar` implements `Foo` + = help: `Bar` implements trait `Foo`: + Foo + Foo + Foo + Foo + Foo + Foo error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr b/tests/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr index 8bacae9e96a70..4bb816bdb80ed 100644 --- a/tests/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr +++ b/tests/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr @@ -6,12 +6,12 @@ LL | Foo::::bar(&1i8); | | | required by a bound introduced by this call | - = help: the following other types implement trait `Foo`: - `i8` implements `Foo` - `i8` implements `Foo` - `i8` implements `Foo` - `i8` implements `Foo` - `i8` implements `Foo` + = help: `i8` implements trait `Foo`: + Foo + Foo + Foo + Foo + Foo error[E0277]: the trait bound `u8: Foo` is not satisfied --> $DIR/issue-39802-show-5-trait-impls.rs:25:21 @@ -21,17 +21,17 @@ LL | Foo::::bar(&1u8); | | | required by a bound introduced by this call | -help: the following other types implement trait `Foo` +help: `u8` implements trait `Foo` --> $DIR/issue-39802-show-5-trait-impls.rs:11:1 | LL | impl Foo for u8 {} - | ^^^^^^^^^^^^^^^^^^^^ `u8` implements `Foo` + | ^^^^^^^^^^^^^^^^^^^^ `Foo` LL | impl Foo for u8 {} - | ^^^^^^^^^^^^^^^^^^^^ `u8` implements `Foo` + | ^^^^^^^^^^^^^^^^^^^^ `Foo` LL | impl Foo for u8 {} - | ^^^^^^^^^^^^^^^^^^^^ `u8` implements `Foo` + | ^^^^^^^^^^^^^^^^^^^^ `Foo` LL | impl Foo for u8 {} - | ^^^^^^^^^^^^^^^^^^^^^ `u8` implements `Foo` + | ^^^^^^^^^^^^^^^^^^^^^ `Foo` error[E0277]: the trait bound `bool: Foo` is not satisfied --> $DIR/issue-39802-show-5-trait-impls.rs:26:21 @@ -41,13 +41,13 @@ LL | Foo::::bar(&true); | | | required by a bound introduced by this call | - = help: the following other types implement trait `Foo`: - `bool` implements `Foo` - `bool` implements `Foo` - `bool` implements `Foo` - `bool` implements `Foo` - `bool` implements `Foo` - `bool` implements `Foo` + = help: `bool` implements trait `Foo`: + Foo + Foo + Foo + Foo + Foo + Foo error: aborting due to 3 previous errors diff --git a/tests/ui/feature-gates/issue-43106-gating-of-bench.stderr b/tests/ui/feature-gates/issue-43106-gating-of-bench.stderr index 912c2746f3835..c51d615846f5a 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-bench.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-bench.stderr @@ -5,7 +5,7 @@ LL | #![bench = "4100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index 1560f2b5f4a77..66d36d9a5d6de 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -37,7 +37,7 @@ #[inline] //~^ ERROR attribute cannot be used on mod inline { - //~^ NOTE the inner attribute doesn't annotate this module + //~^ NOTE the inner attribute doesn't annotate this item mod inner { #![inline] } //~^ ERROR attribute cannot be used on diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 662776e580267..db56f22acfba6 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -24,6 +24,21 @@ LL | #![rustc_main] | = help: `#[rustc_main]` can only be applied to functions +error: `repr` attribute cannot be used at crate level + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:16:1 + | +LL | #![repr()] + | ^^^^^^^^^^ +... +LL | mod inline { + | ------------ the inner attribute doesn't annotate this item + | +help: perhaps you meant to use an outer attribute + | +LL - #![repr()] +LL + #[repr()] + | + error: `#[path]` attribute cannot be used on crates --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:20:1 | @@ -243,21 +258,6 @@ help: remove the `#[no_mangle]` attribute LL - #![no_mangle] | -error: `repr` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:16:1 - | -LL | #![repr()] - | ^^^^^^^^^^ -... -LL | mod inline { - | ------ the inner attribute doesn't annotate this module - | -help: perhaps you meant to use an outer attribute - | -LL - #![repr()] -LL + #[repr()] - | - error[E0517]: attribute should be applied to a struct, enum, or union --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:127:25 | diff --git a/tests/ui/feature-gates/issue-43106-gating-of-test.stderr b/tests/ui/feature-gates/issue-43106-gating-of-test.stderr index 6e706b151b72f..8af140bc93d9b 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-test.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-test.stderr @@ -5,7 +5,7 @@ LL | #![test = "4200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/indexing/index-help.stderr b/tests/ui/indexing/index-help.stderr index 8fc91c1bf3952..65faaec41258e 100644 --- a/tests/ui/indexing/index-help.stderr +++ b/tests/ui/indexing/index-help.stderr @@ -5,13 +5,13 @@ LL | x[0i32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[{integer}]>` is not implemented for `i32` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `Vec<{integer}>` to implement `Index` error: aborting due to 1 previous error diff --git a/tests/ui/indexing/indexing-integral-types.stderr b/tests/ui/indexing/indexing-integral-types.stderr index 3dfc5f5e26e07..604161325afcb 100644 --- a/tests/ui/indexing/indexing-integral-types.stderr +++ b/tests/ui/indexing/indexing-integral-types.stderr @@ -5,13 +5,13 @@ LL | v[3u8]; | ^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[isize]>` is not implemented for `u8` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `Vec` to implement `Index` error[E0277]: the type `[isize]` cannot be indexed by `i8` @@ -21,13 +21,13 @@ LL | v[3i8]; | ^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[isize]>` is not implemented for `i8` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `Vec` to implement `Index` error[E0277]: the type `[isize]` cannot be indexed by `u32` @@ -37,13 +37,13 @@ LL | v[3u32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[isize]>` is not implemented for `u32` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `Vec` to implement `Index` error[E0277]: the type `[isize]` cannot be indexed by `i32` @@ -53,13 +53,13 @@ LL | v[3i32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[isize]>` is not implemented for `i32` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `Vec` to implement `Index` error[E0277]: the type `[u8]` cannot be indexed by `u8` @@ -69,13 +69,13 @@ LL | s.as_bytes()[3u8]; | ^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[u8]>` is not implemented for `u8` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `[u8]` to implement `Index` error[E0277]: the type `[u8]` cannot be indexed by `i8` @@ -85,13 +85,13 @@ LL | s.as_bytes()[3i8]; | ^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[u8]>` is not implemented for `i8` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `[u8]` to implement `Index` error[E0277]: the type `[u8]` cannot be indexed by `u32` @@ -101,13 +101,13 @@ LL | s.as_bytes()[3u32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[u8]>` is not implemented for `u32` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `[u8]` to implement `Index` error[E0277]: the type `[u8]` cannot be indexed by `i32` @@ -117,13 +117,13 @@ LL | s.as_bytes()[3i32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[u8]>` is not implemented for `i32` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `[u8]` to implement `Index` error: aborting due to 8 previous errors diff --git a/tests/ui/indexing/indexing-requires-a-uint.stderr b/tests/ui/indexing/indexing-requires-a-uint.stderr index e5f9f7854f7b4..9e974c8c9bbcc 100644 --- a/tests/ui/indexing/indexing-requires-a-uint.stderr +++ b/tests/ui/indexing/indexing-requires-a-uint.stderr @@ -5,13 +5,13 @@ LL | [0][0u8]; | ^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[{integer}]>` is not implemented for `u8` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `[{integer}]` to implement `Index` error[E0308]: mismatched types diff --git a/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocno.rs b/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocno.rs new file mode 100644 index 0000000000000..ebba4ae92c47f --- /dev/null +++ b/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocno.rs @@ -0,0 +1,10 @@ +//@ compile-flags: -C overflow-checks=no + +#![crate_type = "lib"] +#![feature(new_range_api)] + +use std::range::RangeFromIter; + +pub fn next(iter: &mut RangeFromIter) -> u8 { + iter.next().unwrap() +} diff --git a/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocyes.rs b/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocyes.rs new file mode 100644 index 0000000000000..8eb5392c7bac7 --- /dev/null +++ b/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocyes.rs @@ -0,0 +1,10 @@ +//@ compile-flags: -C overflow-checks=yes + +#![crate_type = "lib"] +#![feature(new_range_api)] + +use std::range::RangeFromIter; + +pub fn next(iter: &mut RangeFromIter) -> u8 { + iter.next().unwrap() +} diff --git a/tests/ui/iterators/invalid-iterator-chain-fixable.stderr b/tests/ui/iterators/invalid-iterator-chain-fixable.stderr index 99bff6b450b56..f22fc2143edf2 100644 --- a/tests/ui/iterators/invalid-iterator-chain-fixable.stderr +++ b/tests/ui/iterators/invalid-iterator-chain-fixable.stderr @@ -33,13 +33,13 @@ LL | println!("{}", scores.sum::()); | required by a bound introduced by this call | = help: the trait `Sum<()>` is not implemented for `i32` -help: the following other types implement trait `Sum` +help: `i32` implements trait `Sum` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum` + = note: `Sum` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum<&i32>` + = note: `Sum<&i32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation @@ -74,13 +74,13 @@ LL | .sum::(), | required by a bound introduced by this call | = help: the trait `Sum<()>` is not implemented for `i32` -help: the following other types implement trait `Sum` +help: `i32` implements trait `Sum` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum` + = note: `Sum` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum<&i32>` + = note: `Sum<&i32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation @@ -115,13 +115,13 @@ LL | println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::()); | required by a bound introduced by this call | = help: the trait `Sum<()>` is not implemented for `i32` -help: the following other types implement trait `Sum` +help: `i32` implements trait `Sum` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum` + = note: `Sum` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum<&i32>` + = note: `Sum<&i32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation diff --git a/tests/ui/iterators/invalid-iterator-chain-with-int-infer.stderr b/tests/ui/iterators/invalid-iterator-chain-with-int-infer.stderr index c94b716e3131f..82269f6253a36 100644 --- a/tests/ui/iterators/invalid-iterator-chain-with-int-infer.stderr +++ b/tests/ui/iterators/invalid-iterator-chain-with-int-infer.stderr @@ -7,13 +7,13 @@ LL | let x = Some(()).iter().map(|()| 1).sum::(); | required by a bound introduced by this call | = help: the trait `Sum<{integer}>` is not implemented for `f32` -help: the following other types implement trait `Sum` +help: `f32` implements trait `Sum` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `f32` implements `Sum` + = note: `Sum` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `f32` implements `Sum<&f32>` + = note: `Sum<&f32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation diff --git a/tests/ui/iterators/invalid-iterator-chain.stderr b/tests/ui/iterators/invalid-iterator-chain.stderr index 7f0c154e255ad..d58fe438a6271 100644 --- a/tests/ui/iterators/invalid-iterator-chain.stderr +++ b/tests/ui/iterators/invalid-iterator-chain.stderr @@ -33,13 +33,13 @@ LL | println!("{}", scores.sum::()); | required by a bound introduced by this call | = help: the trait `Sum<()>` is not implemented for `i32` -help: the following other types implement trait `Sum` +help: `i32` implements trait `Sum` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum` + = note: `Sum` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum<&i32>` + = note: `Sum<&i32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation @@ -73,13 +73,13 @@ LL | .sum::(), | required by a bound introduced by this call | = help: the trait `Sum<()>` is not implemented for `i32` -help: the following other types implement trait `Sum` +help: `i32` implements trait `Sum` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum` + = note: `Sum` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum<&i32>` + = note: `Sum<&i32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation @@ -120,13 +120,13 @@ LL | .sum::(), | required by a bound introduced by this call | = help: the trait `Sum` is not implemented for `i32` -help: the following other types implement trait `Sum` +help: `i32` implements trait `Sum` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum` + = note: `Sum` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum<&i32>` + = note: `Sum<&i32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation @@ -158,13 +158,13 @@ LL | println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::()); | required by a bound introduced by this call | = help: the trait `Sum<()>` is not implemented for `i32` -help: the following other types implement trait `Sum` +help: `i32` implements trait `Sum` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum` + = note: `Sum` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum<&i32>` + = note: `Sum<&i32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation @@ -194,13 +194,13 @@ LL | println!("{}", vec![(), ()].iter().sum::()); | required by a bound introduced by this call | = help: the trait `Sum<&()>` is not implemented for `i32` -help: the following other types implement trait `Sum` +help: `i32` implements trait `Sum` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum` + = note: `Sum` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum<&i32>` + = note: `Sum<&i32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation diff --git a/tests/ui/iterators/rangefrom-overflow-2crates.rs b/tests/ui/iterators/rangefrom-overflow-2crates.rs new file mode 100644 index 0000000000000..c35c96f99322e --- /dev/null +++ b/tests/ui/iterators/rangefrom-overflow-2crates.rs @@ -0,0 +1,40 @@ +//@ run-pass +//@ needs-unwind +//@ aux-build:rangefrom-overflow-2crates-ocno.rs +//@ aux-build:rangefrom-overflow-2crates-ocyes.rs + +// For #154124 +// Test that two crates with different overflow-checks have the same results, +// even when the iterator is passed between them. + +#![feature(new_range_api)] + +extern crate rangefrom_overflow_2crates_ocno; +extern crate rangefrom_overflow_2crates_ocyes; + +use rangefrom_overflow_2crates_ocno::next as next_ocno; +use rangefrom_overflow_2crates_ocyes::next as next_ocyes; + +fn main() { + let mut iter_ocyes = std::range::RangeFrom::from(0_u8..).into_iter(); + let mut iter_ocno = iter_ocyes.clone(); + + for n in 0_u8..=255 { + assert_eq!(n, next_ocno(&mut iter_ocyes.clone())); + assert_eq!(n, next_ocyes(&mut iter_ocyes)); + assert_eq!(n, next_ocyes(&mut iter_ocno.clone())); + assert_eq!(n, next_ocno(&mut iter_ocno)); + } + + // `iter_ocno` should have wrapped + assert_eq!(0, next_ocyes(&mut iter_ocno.clone())); + assert_eq!(0, next_ocno(&mut iter_ocno)); + // `iter_ocyes` should be exhausted, + // which will wrap when called without overflow-checks + assert_eq!(0, next_ocno(&mut iter_ocyes.clone())); + // and panic when called with overflow-checks + let r = std::panic::catch_unwind(move || { + let _ = next_ocyes(&mut iter_ocyes); + }); + assert!(r.is_err()); +} diff --git a/tests/ui/lazy-type-alias/trailing-where-clause.stderr b/tests/ui/lazy-type-alias/trailing-where-clause.stderr index 93cd3145928b7..170bc3bca78d3 100644 --- a/tests/ui/lazy-type-alias/trailing-where-clause.stderr +++ b/tests/ui/lazy-type-alias/trailing-where-clause.stderr @@ -4,13 +4,13 @@ error[E0277]: the trait bound `String: From<()>` is not satisfied LL | let _: Alias<()>; | ^^ the trait `From<()>` is not implemented for `String` | - = help: the following other types implement trait `From`: - `String` implements `From<&String>` - `String` implements `From<&mut str>` - `String` implements `From<&str>` - `String` implements `From>` - `String` implements `From>` - `String` implements `From` + = help: `String` implements trait `From`: + From<&String> + From<&mut str> + From<&str> + From> + From> + From note: required by a bound in `Alias` --> $DIR/trailing-where-clause.rs:8:13 | diff --git a/tests/ui/lowering/issue-121108.stderr b/tests/ui/lowering/issue-121108.stderr index f68655a70021f..e1aa153eede64 100644 --- a/tests/ui/lowering/issue-121108.stderr +++ b/tests/ui/lowering/issue-121108.stderr @@ -5,7 +5,7 @@ LL | #![derive(Clone, Copy)] | ^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | use std::ptr::addr_of; - | ------- the inner attribute doesn't annotate this import + | ---------------------- the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/on-unimplemented/slice-index.stderr b/tests/ui/on-unimplemented/slice-index.stderr index 67b72bd038d80..61d4866f5618b 100644 --- a/tests/ui/on-unimplemented/slice-index.stderr +++ b/tests/ui/on-unimplemented/slice-index.stderr @@ -5,13 +5,13 @@ LL | x[1i32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[i32]>` is not implemented for `i32` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `[i32]` to implement `Index` error[E0277]: the type `[i32]` cannot be indexed by `RangeTo` @@ -21,19 +21,19 @@ LL | x[..1i32]; | ^^^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[i32]>` is not implemented for `RangeTo` -help: the following other types implement trait `SliceIndex` +help: `RangeTo` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `RangeTo` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `RangeTo` implements `SliceIndex` + = note: `SliceIndex` ::: $SRC_DIR/core/src/bstr/traits.rs:LL:COL | = note: in this macro invocation --> $SRC_DIR/core/src/str/traits.rs:LL:COL | - = note: `RangeTo` implements `SliceIndex` + = note: `SliceIndex` = note: required for `[i32]` to implement `Index>` = note: this error originates in the macro `impl_slice_index` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/on-unimplemented/suggest_tuple_wrap_root_obligation.rs b/tests/ui/on-unimplemented/suggest_tuple_wrap_root_obligation.rs index aaaf4d3b2e112..2c898633b5e41 100644 --- a/tests/ui/on-unimplemented/suggest_tuple_wrap_root_obligation.rs +++ b/tests/ui/on-unimplemented/suggest_tuple_wrap_root_obligation.rs @@ -5,7 +5,7 @@ impl From<(u8,)> for Tuple { todo!() } } -impl From<(u8, u8)> for Tuple { //~ HELP the following other types implement trait `From` +impl From<(u8, u8)> for Tuple { //~ HELP `Tuple` implements trait `From` fn from(_: (u8, u8)) -> Self { todo!() } diff --git a/tests/ui/on-unimplemented/suggest_tuple_wrap_root_obligation.stderr b/tests/ui/on-unimplemented/suggest_tuple_wrap_root_obligation.stderr index cf15ac1f279b1..f07db05abad6b 100644 --- a/tests/ui/on-unimplemented/suggest_tuple_wrap_root_obligation.stderr +++ b/tests/ui/on-unimplemented/suggest_tuple_wrap_root_obligation.stderr @@ -11,17 +11,17 @@ help: the trait `From` is not implemented for `Tuple` | LL | struct Tuple; | ^^^^^^^^^^^^ -help: the following other types implement trait `From` +help: `Tuple` implements trait `From` --> $DIR/suggest_tuple_wrap_root_obligation.rs:3:1 | LL | impl From<(u8,)> for Tuple { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `Tuple` implements `From<(u8,)>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `From<(u8,)>` ... LL | impl From<(u8, u8)> for Tuple { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Tuple` implements `From<(u8, u8)>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `From<(u8, u8)>` ... LL | impl From<(u8, u8, u8)> for Tuple { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Tuple` implements `From<(u8, u8, u8)>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `From<(u8, u8, u8)>` = note: required for `u8` to implement `Into` note: required by a bound in `convert_into_tuple` --> $DIR/suggest_tuple_wrap_root_obligation.rs:19:32 diff --git a/tests/ui/on-unimplemented/sum.stderr b/tests/ui/on-unimplemented/sum.stderr index 5e82948352f75..56257079bd314 100644 --- a/tests/ui/on-unimplemented/sum.stderr +++ b/tests/ui/on-unimplemented/sum.stderr @@ -7,13 +7,13 @@ LL | vec![(), ()].iter().sum::(); | required by a bound introduced by this call | = help: the trait `Sum<&()>` is not implemented for `i32` -help: the following other types implement trait `Sum` +help: `i32` implements trait `Sum` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum` + = note: `Sum` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Sum<&i32>` + = note: `Sum<&i32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation @@ -37,13 +37,13 @@ LL | vec![(), ()].iter().product::(); | required by a bound introduced by this call | = help: the trait `Product<&()>` is not implemented for `i32` -help: the following other types implement trait `Product` +help: `i32` implements trait `Product` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Product` + = note: `Product` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | - = note: `i32` implements `Product<&i32>` + = note: `Product<&i32>` ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation diff --git a/tests/ui/proc-macro/issue-89566-suggest-fix-invalid-top-level-macro-attr.stderr b/tests/ui/proc-macro/issue-89566-suggest-fix-invalid-top-level-macro-attr.stderr index 09908160542d4..1d7b5ab8c6af1 100644 --- a/tests/ui/proc-macro/issue-89566-suggest-fix-invalid-top-level-macro-attr.stderr +++ b/tests/ui/proc-macro/issue-89566-suggest-fix-invalid-top-level-macro-attr.stderr @@ -5,7 +5,7 @@ LL | #![derive(Debug)] | ^^^^^^^^^^^^^^^^^ LL | #[allow(dead_code)] LL | struct Test {} - | ---- the inner attribute doesn't annotate this struct + | -------------- the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/span/issue-43927-non-ADT-derive.stderr b/tests/ui/span/issue-43927-non-ADT-derive.stderr index 27ed561f5be8a..8742f34c45c62 100644 --- a/tests/ui/span/issue-43927-non-ADT-derive.stderr +++ b/tests/ui/span/issue-43927-non-ADT-derive.stderr @@ -5,7 +5,7 @@ LL | #![derive(Debug, PartialEq, Eq)] // should be an outer attribute! | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | struct DerivedOn; - | --------- the inner attribute doesn't annotate this struct + | ----------------- the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/str/str-idx.stderr b/tests/ui/str/str-idx.stderr index 97a083ba8ba85..84f698a6e6625 100644 --- a/tests/ui/str/str-idx.stderr +++ b/tests/ui/str/str-idx.stderr @@ -7,13 +7,13 @@ LL | let _: u8 = s[4]; = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `str` to implement `Index<{integer}>` error[E0277]: the type `str` cannot be indexed by `{integer}` @@ -27,13 +27,13 @@ LL | let _ = s.get(4); = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` note: required by a bound in `core::str::::get` --> $SRC_DIR/core/src/str/mod.rs:LL:COL @@ -48,13 +48,13 @@ LL | let _ = s.get_unchecked(4); = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` note: required by a bound in `core::str::::get_unchecked` --> $SRC_DIR/core/src/str/mod.rs:LL:COL diff --git a/tests/ui/str/str-mut-idx.stderr b/tests/ui/str/str-mut-idx.stderr index c9bd66dfbc8aa..87b78915075d2 100644 --- a/tests/ui/str/str-mut-idx.stderr +++ b/tests/ui/str/str-mut-idx.stderr @@ -31,13 +31,13 @@ LL | s[1usize] = bot(); | ^^^^^^ string indices are ranges of `usize` | = help: the trait `SliceIndex` is not implemented for `usize` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `str` to implement `Index` error[E0277]: the type `str` cannot be indexed by `{integer}` @@ -51,13 +51,13 @@ LL | s.get_mut(1); = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` note: required by a bound in `core::str::::get_mut` --> $SRC_DIR/core/src/str/mod.rs:LL:COL @@ -72,13 +72,13 @@ LL | s.get_unchecked_mut(1); = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` note: required by a bound in `core::str::::get_unchecked_mut` --> $SRC_DIR/core/src/str/mod.rs:LL:COL diff --git a/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr index 5eb64c45f9d7f..836d31f81541c 100644 --- a/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr +++ b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr @@ -4,13 +4,13 @@ error[E0277]: the trait bound `str: From<_>` is not satisfied LL | let _ = &str::from("value"); | ^^^ the trait `From<_>` is not implemented for `str` | - = help: the following other types implement trait `From`: - `String` implements `From<&String>` - `String` implements `From<&mut str>` - `String` implements `From<&str>` - `String` implements `From>` - `String` implements `From>` - `String` implements `From` + = help: `String` implements trait `From`: + From<&String> + From<&mut str> + From<&str> + From> + From> + From help: you likely meant to call the associated function `from` for type `&str`, but the code as written calls associated function `from` on type `str` | LL | let _ = <&str>::from("value"); diff --git a/tests/ui/suggestions/into-str.stderr b/tests/ui/suggestions/into-str.stderr index d02d318608297..cfb8f33bfcef3 100644 --- a/tests/ui/suggestions/into-str.stderr +++ b/tests/ui/suggestions/into-str.stderr @@ -7,13 +7,13 @@ LL | foo(String::new()); | required by a bound introduced by this call | = note: to coerce a `String` into a `&str`, use `&*` as a prefix - = help: the following other types implement trait `From`: - `String` implements `From<&String>` - `String` implements `From<&mut str>` - `String` implements `From<&str>` - `String` implements `From>` - `String` implements `From>` - `String` implements `From` + = help: `String` implements trait `From`: + From<&String> + From<&mut str> + From<&str> + From> + From> + From = note: required for `String` to implement `Into<&str>` note: required by a bound in `foo` --> $DIR/into-str.rs:1:31 diff --git a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr index c5984f53f68b5..29e1c6b8a0a32 100644 --- a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr +++ b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr @@ -5,15 +5,15 @@ LL | String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); | ^^ no implementation for `String == T` | = help: the trait `PartialEq` is not implemented for `String` - = help: the following other types implement trait `PartialEq`: - `String` implements `PartialEq<&str>` - `String` implements `PartialEq` - `String` implements `PartialEq` - `String` implements `PartialEq>` - `String` implements `PartialEq` - `String` implements `PartialEq` - `String` implements `PartialEq` - `String` implements `PartialEq` + = help: `String` implements trait `PartialEq`: + PartialEq<&str> + PartialEq + PartialEq + PartialEq> + PartialEq + PartialEq + PartialEq + PartialEq = note: `T` implements `PartialEq` help: consider swapping the equality | diff --git a/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr b/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr index 9f34d27478814..6dd4b1e5d5110 100644 --- a/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr +++ b/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr @@ -20,15 +20,15 @@ LL | .collect::(); | required by a bound introduced by this call | = help: the trait `FromIterator<()>` is not implemented for `String` - = help: the following other types implement trait `FromIterator`: - `String` implements `FromIterator<&char>` - `String` implements `FromIterator<&std::ascii::Char>` - `String` implements `FromIterator<&str>` - `String` implements `FromIterator>` - `String` implements `FromIterator>` - `String` implements `FromIterator` - `String` implements `FromIterator` - `String` implements `FromIterator` + = help: `String` implements trait `FromIterator`: + FromIterator<&char> + FromIterator<&std::ascii::Char> + FromIterator<&str> + FromIterator> + FromIterator> + FromIterator + FromIterator + FromIterator note: the method call chain might not have had the expected associated types --> $DIR/semi-suggestion-when-stmt-and-expr-span-equal.rs:20:10 | diff --git a/tests/ui/suggestions/suggest-dereferencing-index.stderr b/tests/ui/suggestions/suggest-dereferencing-index.stderr index 541c625ebb10b..cee5ffcb2ae8c 100644 --- a/tests/ui/suggestions/suggest-dereferencing-index.stderr +++ b/tests/ui/suggestions/suggest-dereferencing-index.stderr @@ -5,13 +5,13 @@ LL | let one_item_please: i32 = [1, 2, 3][i]; | ^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[{integer}]>` is not implemented for `&usize` -help: the following other types implement trait `SliceIndex` +help: `usize` implements trait `SliceIndex` --> $SRC_DIR/core/src/slice/index.rs:LL:COL | - = note: `usize` implements `SliceIndex<[T]>` + = note: `SliceIndex<[T]>` --> $SRC_DIR/core/src/bstr/traits.rs:LL:COL | - = note: `usize` implements `SliceIndex` + = note: `SliceIndex` = note: required for `[{integer}]` to implement `Index<&usize>` help: dereference this index | diff --git a/tests/ui/traits/explicit-reference-cast.rs b/tests/ui/traits/explicit-reference-cast.rs index efb4f10bea67e..b9ccc1827a555 100644 --- a/tests/ui/traits/explicit-reference-cast.rs +++ b/tests/ui/traits/explicit-reference-cast.rs @@ -7,7 +7,7 @@ pub struct ToolA(PathBuf); //~^ HELP the trait `From<&PathBuf>` is not implemented for `ToolA` impl From<&Path> for ToolA { - //~^ HELP the following other types implement trait `From` + //~^ HELP `ToolA` implements trait `From` fn from(p: &Path) -> ToolA { ToolA(p.to_path_buf()) } diff --git a/tests/ui/traits/explicit-reference-cast.stderr b/tests/ui/traits/explicit-reference-cast.stderr index 924de3d5bbe3b..78eb25b0243de 100644 --- a/tests/ui/traits/explicit-reference-cast.stderr +++ b/tests/ui/traits/explicit-reference-cast.stderr @@ -10,14 +10,14 @@ help: the trait `From<&PathBuf>` is not implemented for `ToolA` | LL | pub struct ToolA(PathBuf); | ^^^^^^^^^^^^^^^^ -help: the following other types implement trait `From` +help: `ToolA` implements trait `From` --> $DIR/explicit-reference-cast.rs:9:1 | LL | impl From<&Path> for ToolA { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `ToolA` implements `From<&Path>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `From<&Path>` ... LL | impl From<&str> for ToolA { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ `ToolA` implements `From<&str>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ `From<&str>` error[E0277]: the trait bound `ToolB: TryFrom<&PathBuf>` is not satisfied --> $DIR/explicit-reference-cast.rs:43:13 diff --git a/tests/ui/traits/inheritance/repeated-supertrait-ambig.stderr b/tests/ui/traits/inheritance/repeated-supertrait-ambig.stderr index e58f5c3fe90f2..9b27491f5046c 100644 --- a/tests/ui/traits/inheritance/repeated-supertrait-ambig.stderr +++ b/tests/ui/traits/inheritance/repeated-supertrait-ambig.stderr @@ -6,14 +6,14 @@ LL | c.same_as(22) | | | required by a bound introduced by this call | -help: the following other types implement trait `CompareTo` +help: `i64` implements trait `CompareTo` --> $DIR/repeated-supertrait-ambig.rs:15:1 | LL | impl CompareTo for i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `i64` implements `CompareTo` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CompareTo` ... LL | impl CompareTo for i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `i64` implements `CompareTo` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CompareTo` error[E0277]: the trait bound `C: CompareTo` is not satisfied --> $DIR/repeated-supertrait-ambig.rs:30:15 @@ -34,14 +34,14 @@ error[E0277]: the trait bound `dyn CompareToInts: CompareTo` is not satisfi LL | ::same_as(c, 22) | ^^^^^^^^^^^^^^^^^ the trait `CompareTo` is not implemented for `dyn CompareToInts` | -help: the following other types implement trait `CompareTo` +help: `i64` implements trait `CompareTo` --> $DIR/repeated-supertrait-ambig.rs:15:1 | LL | impl CompareTo for i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `i64` implements `CompareTo` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CompareTo` ... LL | impl CompareTo for i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `i64` implements `CompareTo` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CompareTo` error[E0277]: the trait bound `C: CompareTo` is not satisfied --> $DIR/repeated-supertrait-ambig.rs:38:24 @@ -64,14 +64,14 @@ LL | assert_eq!(22_i64.same_as(22), true); | | | required by a bound introduced by this call | -help: the following other types implement trait `CompareTo` +help: `i64` implements trait `CompareTo` --> $DIR/repeated-supertrait-ambig.rs:15:1 | LL | impl CompareTo for i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `i64` implements `CompareTo` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CompareTo` ... LL | impl CompareTo for i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `i64` implements `CompareTo` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CompareTo` error: aborting due to 5 previous errors diff --git a/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr b/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr index 42b76a8f7ff67..04a4c70c6d7bf 100644 --- a/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr +++ b/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr @@ -5,7 +5,7 @@ LL | impls_trait::>() | ^^^^^^^ cannot infer type for struct `Head<_>` | = note: cannot satisfy `Head<_>: Trait` -help: the following types implement trait `Trait` +help: `Head` implements trait `Trait` --> $DIR/forced_ambiguity-use-head-maybe-cause.rs:23:1 | LL | / impl Trait for Head diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr index 8b24e682c769f..cea95eb365e6a 100644 --- a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr +++ b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr @@ -21,7 +21,7 @@ help: the trait `Trait` is not implemented for `MultipleCandidates` | LL | struct MultipleCandidates; | ^^^^^^^^^^^^^^^^^^^^^^^^^ -help: the following other types implement trait `Trait` +help: `MultipleCandidates` implements trait `Trait` --> $DIR/inductive-cycle-but-err.rs:26:1 | LL | / impl Trait for MultipleCandidates diff --git a/tests/ui/traits/next-solver/unevaluated-const-impl-trait-ref.fails.stderr b/tests/ui/traits/next-solver/unevaluated-const-impl-trait-ref.fails.stderr index 8202b6ecb5d60..ce68c2f435fa3 100644 --- a/tests/ui/traits/next-solver/unevaluated-const-impl-trait-ref.fails.stderr +++ b/tests/ui/traits/next-solver/unevaluated-const-impl-trait-ref.fails.stderr @@ -4,13 +4,13 @@ error[E0277]: the trait bound `(): Trait<1>` is not satisfied LL | needs::<1>(); | ^ the trait `Trait<1>` is not implemented for `()` | -help: the following other types implement trait `Trait` +help: `()` implements trait `Trait` --> $DIR/unevaluated-const-impl-trait-ref.rs:7:1 | LL | impl Trait<{ 1 - 1 }> for () {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` implements `Trait<0>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Trait<0>` LL | impl Trait<{ 1 + 1 }> for () {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` implements `Trait<2>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Trait<2>` note: required by a bound in `needs` --> $DIR/unevaluated-const-impl-trait-ref.rs:10:38 | diff --git a/tests/ui/traits/question-mark-result-err-mismatch.rs b/tests/ui/traits/question-mark-result-err-mismatch.rs index dfea4b93f46da..f9ca6e0ab4483 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.rs +++ b/tests/ui/traits/question-mark-result-err-mismatch.rs @@ -35,7 +35,7 @@ fn bar() -> Result<(), String> { //~ NOTE expected `String` because of this //~| NOTE the trait `From<()>` is not implemented for `String` //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait //~| NOTE required for `Result<(), String>` to implement `FromResidual>` - //~| HELP the following other types implement trait `From`: + //~| HELP `String` implements trait `From`: Ok(one) } diff --git a/tests/ui/traits/question-mark-result-err-mismatch.stderr b/tests/ui/traits/question-mark-result-err-mismatch.stderr index be3f17cfc5274..3739b508a8689 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.stderr +++ b/tests/ui/traits/question-mark-result-err-mismatch.stderr @@ -30,13 +30,13 @@ LL | .map_err(|_| ())?; | this can't be annotated with `?` because it has type `Result<_, ()>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = help: the following other types implement trait `From`: - `String` implements `From<&String>` - `String` implements `From<&mut str>` - `String` implements `From<&str>` - `String` implements `From>` - `String` implements `From>` - `String` implements `From` + = help: `String` implements trait `From`: + From<&String> + From<&mut str> + From<&str> + From> + From> + From = note: required for `Result<(), String>` to implement `FromResidual>` error[E0277]: `?` couldn't convert the error to `String` diff --git a/tests/ui/traits/question-mark-span-144304.stderr b/tests/ui/traits/question-mark-span-144304.stderr index a412da0d235db..37452cda8a6db 100644 --- a/tests/ui/traits/question-mark-span-144304.stderr +++ b/tests/ui/traits/question-mark-span-144304.stderr @@ -9,12 +9,12 @@ LL | Err("str").map_err(|e| e)?; | this has type `Result<_, &str>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = help: the following other types implement trait `From`: - `i32` implements `From` - `i32` implements `From` - `i32` implements `From` - `i32` implements `From` - `i32` implements `From` + = help: `i32` implements trait `From`: + From + From + From + From + From error[E0277]: `?` couldn't convert the error to `i32` --> $DIR/question-mark-span-144304.rs:4:42 @@ -29,12 +29,12 @@ LL | Err("str").map_err(|e| e.to_string())?; | this has type `Result<_, &str>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = help: the following other types implement trait `From`: - `i32` implements `From` - `i32` implements `From` - `i32` implements `From` - `i32` implements `From` - `i32` implements `From` + = help: `i32` implements trait `From`: + From + From + From + From + From error: aborting due to 2 previous errors diff --git a/tests/ui/try-trait/bad-interconversion.stderr b/tests/ui/try-trait/bad-interconversion.stderr index a566800da53e4..e1cc7a45e5de9 100644 --- a/tests/ui/try-trait/bad-interconversion.stderr +++ b/tests/ui/try-trait/bad-interconversion.stderr @@ -9,16 +9,16 @@ LL | Ok(Err(123_i32)?) | this can't be annotated with `?` because it has type `Result<_, i32>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait -help: the following other types implement trait `From` +help: `u8` implements trait `From` --> $SRC_DIR/core/src/convert/num.rs:LL:COL | - = note: `u8` implements `From` + = note: `From` ::: $SRC_DIR/core/src/convert/num.rs:LL:COL | = note: in this macro invocation --> $SRC_DIR/core/src/ascii/ascii_char.rs:LL:COL | - = note: `u8` implements `From` + = note: `From` ::: $SRC_DIR/core/src/ascii/ascii_char.rs:LL:COL | = note: in this macro invocation diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr index d17a821943532..fc6bf3f189026 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr @@ -9,14 +9,14 @@ help: the trait `Trait` is not implemented for `Foo` | LL | struct Foo; | ^^^^^^^^^^ -help: the following other types implement trait `Trait` +help: `Foo` implements trait `Trait` --> $DIR/constrain_in_projection2.rs:18:1 | LL | impl Trait<()> for Foo { - | ^^^^^^^^^^^^^^^^^^^^^^ `Foo` implements `Trait<()>` + | ^^^^^^^^^^^^^^^^^^^^^^ `Trait<()>` ... LL | impl Trait for Foo { - | ^^^^^^^^^^^^^^^^^^^^^^^ `Foo` implements `Trait` + | ^^^^^^^^^^^^^^^^^^^^^^^ `Trait` error[E0277]: the trait bound `Foo: Trait` is not satisfied --> $DIR/constrain_in_projection2.rs:28:13 @@ -29,14 +29,14 @@ help: the trait `Trait` is not implemented for `Foo` | LL | struct Foo; | ^^^^^^^^^^ -help: the following other types implement trait `Trait` +help: `Foo` implements trait `Trait` --> $DIR/constrain_in_projection2.rs:18:1 | LL | impl Trait<()> for Foo { - | ^^^^^^^^^^^^^^^^^^^^^^ `Foo` implements `Trait<()>` + | ^^^^^^^^^^^^^^^^^^^^^^ `Trait<()>` ... LL | impl Trait for Foo { - | ^^^^^^^^^^^^^^^^^^^^^^^ `Foo` implements `Trait` + | ^^^^^^^^^^^^^^^^^^^^^^^ `Trait` error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr index 4cc69daffe622..674442784ae7f 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr @@ -7,13 +7,13 @@ LL | LL | () | -- return type was inferred to be `()` here | -help: the following other types implement trait `Foo` +help: `()` implements trait `Foo` --> $DIR/nested-tait-inference2.rs:14:1 | LL | impl Foo<()> for () {} - | ^^^^^^^^^^^^^^^^^^^ `()` implements `Foo<()>` + | ^^^^^^^^^^^^^^^^^^^ `Foo<()>` LL | impl Foo for () {} - | ^^^^^^^^^^^^^^^^^^^^ `()` implements `Foo` + | ^^^^^^^^^^^^^^^^^^^^ `Foo` error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-90101.stderr b/tests/ui/typeck/issue-90101.stderr index 2e140461c1d96..fe9af5a5f4f56 100644 --- a/tests/ui/typeck/issue-90101.stderr +++ b/tests/ui/typeck/issue-90101.stderr @@ -6,12 +6,12 @@ LL | func(Path::new("hello").to_path_buf().to_string_lossy(), "world") | | | required by a bound introduced by this call | - = help: the following other types implement trait `From`: - `PathBuf` implements `From<&T>` - `PathBuf` implements `From>` - `PathBuf` implements `From>` - `PathBuf` implements `From` - `PathBuf` implements `From` + = help: `PathBuf` implements trait `From`: + From<&T> + From> + From> + From + From = note: required for `Cow<'_, str>` to implement `Into` note: required by a bound in `func` --> $DIR/issue-90101.rs:3:20 diff --git a/tests/ui/where-clauses/cfg_attribute.a.stderr b/tests/ui/where-clauses/cfg_attribute.a.stderr index ee81072e70062..5bfa65df6032b 100644 --- a/tests/ui/where-clauses/cfg_attribute.a.stderr +++ b/tests/ui/where-clauses/cfg_attribute.a.stderr @@ -93,39 +93,39 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:65:5 + --> $DIR/cfg_attribute.rs:42:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:68:5 + --> $DIR/cfg_attribute.rs:45:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:99:5 + --> $DIR/cfg_attribute.rs:53:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:102:5 + --> $DIR/cfg_attribute.rs:56:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:113:5 + --> $DIR/cfg_attribute.rs:65:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:116:5 + --> $DIR/cfg_attribute.rs:68:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -141,39 +141,39 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:128:5 + --> $DIR/cfg_attribute.rs:75:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:131:5 + --> $DIR/cfg_attribute.rs:78:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:143:5 + --> $DIR/cfg_attribute.rs:86:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:146:5 + --> $DIR/cfg_attribute.rs:89:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:154:5 + --> $DIR/cfg_attribute.rs:99:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:157:5 + --> $DIR/cfg_attribute.rs:102:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:177:5 + --> $DIR/cfg_attribute.rs:113:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:180:5 + --> $DIR/cfg_attribute.rs:116:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -205,55 +205,55 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:42:9 + --> $DIR/cfg_attribute.rs:128:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:45:9 + --> $DIR/cfg_attribute.rs:131:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:53:9 + --> $DIR/cfg_attribute.rs:143:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:56:9 + --> $DIR/cfg_attribute.rs:146:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:75:9 + --> $DIR/cfg_attribute.rs:154:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:78:9 + --> $DIR/cfg_attribute.rs:157:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:86:9 + --> $DIR/cfg_attribute.rs:164:9 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -261,7 +261,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:89:9 + --> $DIR/cfg_attribute.rs:167:9 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -269,18 +269,18 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:164:9 + --> $DIR/cfg_attribute.rs:177:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:167:9 + --> $DIR/cfg_attribute.rs:180:5 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported diff --git a/tests/ui/where-clauses/cfg_attribute.b.stderr b/tests/ui/where-clauses/cfg_attribute.b.stderr index ee81072e70062..5bfa65df6032b 100644 --- a/tests/ui/where-clauses/cfg_attribute.b.stderr +++ b/tests/ui/where-clauses/cfg_attribute.b.stderr @@ -93,39 +93,39 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:65:5 + --> $DIR/cfg_attribute.rs:42:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:68:5 + --> $DIR/cfg_attribute.rs:45:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:99:5 + --> $DIR/cfg_attribute.rs:53:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:102:5 + --> $DIR/cfg_attribute.rs:56:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:113:5 + --> $DIR/cfg_attribute.rs:65:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:116:5 + --> $DIR/cfg_attribute.rs:68:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -141,39 +141,39 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:128:5 + --> $DIR/cfg_attribute.rs:75:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:131:5 + --> $DIR/cfg_attribute.rs:78:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:143:5 + --> $DIR/cfg_attribute.rs:86:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:146:5 + --> $DIR/cfg_attribute.rs:89:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:154:5 + --> $DIR/cfg_attribute.rs:99:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:157:5 + --> $DIR/cfg_attribute.rs:102:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:177:5 + --> $DIR/cfg_attribute.rs:113:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:180:5 + --> $DIR/cfg_attribute.rs:116:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -205,55 +205,55 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:42:9 + --> $DIR/cfg_attribute.rs:128:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:45:9 + --> $DIR/cfg_attribute.rs:131:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:53:9 + --> $DIR/cfg_attribute.rs:143:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:56:9 + --> $DIR/cfg_attribute.rs:146:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:75:9 + --> $DIR/cfg_attribute.rs:154:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:78:9 + --> $DIR/cfg_attribute.rs:157:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:86:9 + --> $DIR/cfg_attribute.rs:164:9 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -261,7 +261,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:89:9 + --> $DIR/cfg_attribute.rs:167:9 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -269,18 +269,18 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:164:9 + --> $DIR/cfg_attribute.rs:177:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:167:9 + --> $DIR/cfg_attribute.rs:180:5 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported diff --git a/tests/ui/where-clauses/unsupported_attribute.stderr b/tests/ui/where-clauses/unsupported_attribute.stderr index e55a7c380c2be..11f13ffa4d007 100644 --- a/tests/ui/where-clauses/unsupported_attribute.stderr +++ b/tests/ui/where-clauses/unsupported_attribute.stderr @@ -10,6 +10,22 @@ error: expected non-macro attribute, found attribute macro `derive` LL | #[derive(Clone)] 'a: 'static, | ^^^^^^ not a non-macro attribute +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:13:5 + | +LL | #[doc = "doc"] T: Trait, + | ^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:14:5 + | +LL | #[doc = "doc"] 'a: 'static, + | ^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + error: `#[ignore]` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:15:5 | @@ -58,6 +74,22 @@ LL | #[macro_use] 'a: 'static, | = help: `#[macro_use]` can be applied to crates, extern crates, and modules +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:21:5 + | +LL | #[allow(unused)] T: Trait, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:22:5 + | +LL | #[allow(unused)] 'a: 'static, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + error: `#[deprecated]` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:23:5 | @@ -90,38 +122,6 @@ LL | #[automatically_derived] 'a: 'static, | = help: `#[automatically_derived]` can only be applied to trait impl blocks -error: most attributes are not supported in `where` clauses - --> $DIR/unsupported_attribute.rs:13:5 - | -LL | #[doc = "doc"] T: Trait, - | ^^^^^^^^^^^^^^ - | - = help: only `#[cfg]` and `#[cfg_attr]` are supported - -error: most attributes are not supported in `where` clauses - --> $DIR/unsupported_attribute.rs:14:5 - | -LL | #[doc = "doc"] 'a: 'static, - | ^^^^^^^^^^^^^^ - | - = help: only `#[cfg]` and `#[cfg_attr]` are supported - -error: most attributes are not supported in `where` clauses - --> $DIR/unsupported_attribute.rs:21:5 - | -LL | #[allow(unused)] T: Trait, - | ^^^^^^^^^^^^^^^^ - | - = help: only `#[cfg]` and `#[cfg_attr]` are supported - -error: most attributes are not supported in `where` clauses - --> $DIR/unsupported_attribute.rs:22:5 - | -LL | #[allow(unused)] 'a: 'static, - | ^^^^^^^^^^^^^^^^ - | - = help: only `#[cfg]` and `#[cfg_attr]` are supported - error: most attributes are not supported in `where` clauses --> $DIR/unsupported_attribute.rs:27:5 |