diff --git a/Cargo.lock b/Cargo.lock index c987bf44ec00d..717e29db3aba7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3943,6 +3943,7 @@ dependencies = [ "rustc_hir", "rustc_index", "rustc_infer", + "rustc_macros", "rustc_middle", "rustc_serialize", "rustc_session", diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 69434280b2144..3c324359565c1 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -295,8 +295,18 @@ impl<'ll> CodegenCx<'ll, '_> { llvm::set_thread_local_mode(g, self.tls_model); } + let dso_local = unsafe { self.should_assume_dso_local(g, true) }; + if dso_local { + unsafe { + llvm::LLVMRustSetDSOLocal(g, true); + } + } + if !def_id.is_local() { let needs_dll_storage_attr = self.use_dll_storage_attrs && !self.tcx.is_foreign_item(def_id) && + // Local definitions can never be imported, so we must not apply + // the DLLImport annotation. + !dso_local && // ThinLTO can't handle this workaround in all cases, so we don't // emit the attrs. Instead we make them unnecessary by disallowing // dynamic linking when linker plugin based LTO is enabled. @@ -340,12 +350,6 @@ impl<'ll> CodegenCx<'ll, '_> { } } - unsafe { - if self.should_assume_dso_local(g, true) { - llvm::LLVMRustSetDSOLocal(g, true); - } - } - self.instances.borrow_mut().insert(instance, g); g } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 36956f5dd6d96..078beadf3fc12 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceC use rustc_middle::mir::*; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt}; -use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeVisitable}; +use rustc_middle::ty::{Binder, TraitRef, TypeVisitable}; use rustc_mir_dataflow::{self, Analysis}; use rustc_span::{sym, Span, Symbol}; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; @@ -726,11 +726,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } let trait_ref = TraitRef::from_method(tcx, trait_id, substs); - let poly_trait_pred = Binder::dummy(TraitPredicate { - trait_ref, - constness: ty::BoundConstness::ConstIfConst, - polarity: ty::ImplPolarity::Positive, - }); + let poly_trait_pred = + Binder::dummy(trait_ref).with_constness(ty::BoundConstness::ConstIfConst); let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, poly_trait_pred); @@ -819,9 +816,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { tcx, ObligationCause::dummy_with_span(*fn_span), param_env, - tcx.mk_predicate( - poly_trait_pred.map_bound(ty::PredicateKind::Trait), - ), + poly_trait_pred, ); // improve diagnostics by showing what failed. Our requirements are stricter this time diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index c62c665158779..bfc950eff5c06 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -13,10 +13,9 @@ use rustc_middle::mir; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::{ - suggest_constraining_type_param, Adt, Closure, DefIdTree, FnDef, FnPtr, Param, TraitPredicate, - Ty, + suggest_constraining_type_param, Adt, Closure, DefIdTree, FnDef, FnPtr, Param, Ty, }; -use rustc_middle::ty::{Binder, BoundConstness, ImplPolarity, TraitRef}; +use rustc_middle::ty::{Binder, TraitRef}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{BytePos, Pos, Span, Symbol}; @@ -150,11 +149,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { tcx, ObligationCause::dummy(), param_env, - Binder::dummy(TraitPredicate { - trait_ref, - constness: BoundConstness::NotConst, - polarity: ImplPolarity::Positive, - }), + Binder::dummy(trait_ref), ); let infcx = tcx.infer_ctxt().build(); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index e7b3df9b7288b..8ca3fdf400eb3 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -157,11 +157,8 @@ impl Qualif for NeedsNonConstDrop { cx.tcx, ObligationCause::dummy_with_span(cx.body.span), cx.param_env, - ty::Binder::dummy(ty::TraitPredicate { - trait_ref: cx.tcx.at(cx.body.span).mk_trait_ref(LangItem::Destruct, [ty]), - constness: ty::BoundConstness::ConstIfConst, - polarity: ty::ImplPolarity::Positive, - }), + ty::Binder::dummy(cx.tcx.at(cx.body.span).mk_trait_ref(LangItem::Destruct, [ty])) + .with_constness(ty::BoundConstness::ConstIfConst), ); let infcx = cx.tcx.infer_ctxt().build(); diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl new file mode 100644 index 0000000000000..c6b26dd09247d --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl @@ -0,0 +1,300 @@ +mir_build_unconditional_recursion = function cannot return without recursing + .label = cannot return without recursing + .help = a `loop` may express intention better if this is on purpose + +mir_build_unconditional_recursion_call_site_label = recursive call site + +mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe = + call to unsafe function `{$function}` is unsafe and requires unsafe block (error E0133) + .note = consult the function's documentation for information on how to avoid undefined behavior + .label = call to unsafe function + +mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless = + call to unsafe function is unsafe and requires unsafe block (error E0133) + .note = consult the function's documentation for information on how to avoid undefined behavior + .label = call to unsafe function + +mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe = + use of inline assembly is unsafe and requires unsafe block (error E0133) + .note = inline assembly is entirely unchecked and can cause undefined behavior + .label = use of inline assembly + +mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe = + initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe + block (error E0133) + .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior + .label = initializing type with `rustc_layout_scalar_valid_range` attr + +mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe = + use of mutable static is unsafe and requires unsafe block (error E0133) + .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + .label = use of mutable static + +mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe = + use of extern static is unsafe and requires unsafe block (error E0133) + .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + .label = use of extern static + +mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe = + dereference of raw pointer is unsafe and requires unsafe block (error E0133) + .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + .label = dereference of raw pointer + +mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe = + access to union field is unsafe and requires unsafe block (error E0133) + .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior + .label = access to union field + +mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe = + mutation of layout constrained field is unsafe and requires unsafe block (error E0133) + .note = mutating layout constrained fields cannot statically be checked for valid values + .label = mutation of layout constrained field + +mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe = + borrow of layout constrained field with interior mutability is unsafe and requires unsafe block (error E0133) + .note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values + .label = borrow of layout constrained field with interior mutability + +mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe = + call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block (error E0133) + .note = can only be called if the required target features are available + .label = call to function with `#[target_feature]` + +mir_build_call_to_unsafe_fn_requires_unsafe = + call to unsafe function `{$function}` is unsafe and requires unsafe block + .note = consult the function's documentation for information on how to avoid undefined behavior + .label = call to unsafe function + +mir_build_call_to_unsafe_fn_requires_unsafe_nameless = + call to unsafe function is unsafe and requires unsafe block + .note = consult the function's documentation for information on how to avoid undefined behavior + .label = call to unsafe function + +mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + call to unsafe function `{$function}` is unsafe and requires unsafe function or block + .note = consult the function's documentation for information on how to avoid undefined behavior + .label = call to unsafe function + +mir_build_call_to_unsafe_fn_requires_unsafe_nameless_unsafe_op_in_unsafe_fn_allowed = + call to unsafe function is unsafe and requires unsafe function or block + .note = consult the function's documentation for information on how to avoid undefined behavior + .label = call to unsafe function + +mir_build_inline_assembly_requires_unsafe = + use of inline assembly is unsafe and requires unsafe block + .note = inline assembly is entirely unchecked and can cause undefined behavior + .label = use of inline assembly + +mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + use of inline assembly is unsafe and requires unsafe function or block + .note = inline assembly is entirely unchecked and can cause undefined behavior + .label = use of inline assembly + +mir_build_initializing_type_with_requires_unsafe = + initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block + .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior + .label = initializing type with `rustc_layout_scalar_valid_range` attr + +mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block + .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior + .label = initializing type with `rustc_layout_scalar_valid_range` attr + +mir_build_mutable_static_requires_unsafe = + use of mutable static is unsafe and requires unsafe block + .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + .label = use of mutable static + +mir_build_mutable_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + use of mutable static is unsafe and requires unsafe function or block + .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + .label = use of mutable static + +mir_build_extern_static_requires_unsafe = + use of extern static is unsafe and requires unsafe block + .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + .label = use of extern static + +mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + use of extern static is unsafe and requires unsafe function or block + .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + .label = use of extern static + +mir_build_deref_raw_pointer_requires_unsafe = + dereference of raw pointer is unsafe and requires unsafe block + .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + .label = dereference of raw pointer + +mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + dereference of raw pointer is unsafe and requires unsafe function or block + .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + .label = dereference of raw pointer + +mir_build_union_field_requires_unsafe = + access to union field is unsafe and requires unsafe block + .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior + .label = access to union field + +mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + access to union field is unsafe and requires unsafe function or block + .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior + .label = access to union field + +mir_build_mutation_of_layout_constrained_field_requires_unsafe = + mutation of layout constrained field is unsafe and requires unsafe block + .note = mutating layout constrained fields cannot statically be checked for valid values + .label = mutation of layout constrained field + +mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + mutation of layout constrained field is unsafe and requires unsafe function or block + .note = mutating layout constrained fields cannot statically be checked for valid values + .label = mutation of layout constrained field + +mir_build_borrow_of_layout_constrained_field_requires_unsafe = + borrow of layout constrained field with interior mutability is unsafe and requires unsafe block + .note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values + .label = borrow of layout constrained field with interior mutability + +mir_build_borrow_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + borrow of layout constrained field with interior mutability is unsafe and requires unsafe function or block + .note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values + .label = borrow of layout constrained field with interior mutability + +mir_build_call_to_fn_with_requires_unsafe = + call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block + .note = can only be called if the required target features are available + .label = call to function with `#[target_feature]` + +mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe function or block + .note = can only be called if the required target features are available + .label = call to function with `#[target_feature]` + +mir_build_unused_unsafe = unnecessary `unsafe` block + .label = unnecessary `unsafe` block + +mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `unsafe` block +mir_build_unused_unsafe_enclosing_fn_label = because it's nested under this `unsafe` fn + +mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty + .def_note = `{$peeled_ty}` defined here + .type_note = the matched value is of type `{$ty}` + .non_exhaustive_type_note = the matched value is of type `{$ty}`, which is marked as non-exhaustive + .reference_note = references are always considered inhabited + .suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + .help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern + +mir_build_static_in_pattern = statics cannot be referenced in patterns + +mir_build_assoc_const_in_pattern = associated consts cannot be referenced in patterns + +mir_build_const_param_in_pattern = const parameters cannot be referenced in patterns + +mir_build_non_const_path = runtime values cannot be referenced in patterns + +mir_build_unreachable_pattern = unreachable pattern + .label = unreachable pattern + .catchall_label = matches any value + +mir_build_const_pattern_depends_on_generic_parameter = + constant pattern depends on a generic parameter + +mir_build_could_not_eval_const_pattern = could not evaluate constant pattern + +mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper = + lower range bound must be less than or equal to upper + .label = lower bound larger than upper bound + .teach_note = When matching against a range, the compiler verifies that the range is non-empty. Range patterns include both end-points, so this is equivalent to requiring the start of the range to be less than or equal to the end of the range. + +mir_build_lower_range_bound_must_be_less_than_upper = lower range bound must be less than upper + +mir_build_leading_irrefutable_let_patterns = leading irrefutable {$count -> + [one] pattern + *[other] patterns + } in let chain + .note = {$count -> + [one] this pattern + *[other] these patterns + } will always match + .help = consider moving {$count -> + [one] it + *[other] them + } outside of the construct + +mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count -> + [one] pattern + *[other] patterns + } in let chain + .note = {$count -> + [one] this pattern + *[other] these patterns + } will always match + .help = consider moving {$count -> + [one] it + *[other] them + } into the body + +mir_build_bindings_with_variant_name = + pattern binding `{$ident}` is named the same as one of the variants of the type `{$ty_path}` + .suggestion = to match on the variant, qualify the path + +mir_build_irrefutable_let_patterns_generic_let = irrefutable `let` {$count -> + [one] pattern + *[other] patterns + } + .note = {$count -> + [one] this pattern + *[other] these patterns + } will always match, so the `let` is useless + .help = consider removing `let` + +mir_build_irrefutable_let_patterns_if_let = irrefutable `if let` {$count -> + [one] pattern + *[other] patterns + } + .note = {$count -> + [one] this pattern + *[other] these patterns + } will always match, so the `if let` is useless + .help = consider replacing the `if let` with a `let` + +mir_build_irrefutable_let_patterns_if_let_guard = irrefutable `if let` guard {$count -> + [one] pattern + *[other] patterns + } + .note = {$count -> + [one] this pattern + *[other] these patterns + } will always match, so the guard is useless + .help = consider removing the guard and adding a `let` inside the match arm + +mir_build_irrefutable_let_patterns_let_else = irrefutable `let...else` {$count -> + [one] pattern + *[other] patterns + } + .note = {$count -> + [one] this pattern + *[other] these patterns + } will always match, so the `else` clause is useless + .help = consider removing the `else` clause + +mir_build_irrefutable_let_patterns_while_let = irrefutable `while let` {$count -> + [one] pattern + *[other] patterns + } + .note = {$count -> + [one] this pattern + *[other] these patterns + } will always match, so the loop will never exit + .help = consider instead using a `loop {"{"} ... {"}"}` with a `let` inside it + +mir_build_borrow_of_moved_value = borrow of moved value + .label = value moved into `{$name}` here + .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait + .value_borrowed_label = value borrowed here after move + +mir_build_multiple_mut_borrows = cannot borrow value as mutable more than once at a time + .label = first mutable borrow, by `{$name}`, occurs here + .mutable_borrow = another mutable borrow, by `{$name_mut}`, occurs here + .immutable_borrow = also borrowed as immutable, by `{$name_immut}`, here + .moved = also moved into `{$name_moved}` here diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 418ba3c74d776..25d0e736e599f 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -57,6 +57,7 @@ fluent_messages! { lint => "../locales/en-US/lint.ftl", metadata => "../locales/en-US/metadata.ftl", middle => "../locales/en-US/middle.ftl", + mir_build => "../locales/en-US/mir_build.ftl", mir_dataflow => "../locales/en-US/mir_dataflow.ftl", monomorphize => "../locales/en-US/monomorphize.ftl", parse => "../locales/en-US/parse.ftl", diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index bf4e5126bfa10..de386e2d13556 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -218,14 +218,11 @@ fn compare_predicate_entailment<'tcx>( debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds()); - let mut selcx = traits::SelectionContext::new(&infcx); let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs); for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) { let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id); - let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, normalize_cause, predicate); + let predicate = ocx.normalize(normalize_cause, param_env, predicate); - ocx.register_obligations(obligations); let cause = ObligationCause::new( span, impl_m_hir_id, @@ -1555,14 +1552,11 @@ fn compare_type_predicate_entailment<'tcx>( debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds()); - let mut selcx = traits::SelectionContext::new(&infcx); - assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len()); for (span, predicate) in std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates) { let cause = ObligationCause::misc(span, impl_ty_hir_id); - let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, cause, predicate); + let predicate = ocx.normalize(cause, param_env, predicate); let cause = ObligationCause::new( span, @@ -1573,7 +1567,6 @@ fn compare_type_predicate_entailment<'tcx>( kind: impl_ty.kind, }, ); - ocx.register_obligations(obligations); ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate)); } @@ -1756,7 +1749,6 @@ pub fn check_type_bounds<'tcx>( let assumed_wf_types = ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty.def_id.expect_local()); - let mut selcx = traits::SelectionContext::new(&infcx); let normalize_cause = ObligationCause::new( impl_ty_span, impl_ty_hir_id, @@ -1785,16 +1777,11 @@ pub fn check_type_bounds<'tcx>( debug!("check_type_bounds: item_bounds={:?}", obligations); for mut obligation in util::elaborate_obligations(tcx, obligations) { - let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( - &mut selcx, - normalize_param_env, - normalize_cause.clone(), - obligation.predicate, - ); + let normalized_predicate = + ocx.normalize(normalize_cause.clone(), normalize_param_env, obligation.predicate); debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); obligation.predicate = normalized_predicate; - ocx.register_obligations(obligations); ocx.register_obligation(obligation); } // Check that all obligations are satisfied by the implementation's diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 7119f3a238697..d23c41ed69014 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1784,7 +1784,7 @@ fn receiver_is_implemented<'tcx>( let tcx = wfcx.tcx(); let trait_ref = ty::Binder::dummy(tcx.mk_trait_ref(receiver_trait_def_id, [receiver_ty])); - let obligation = traits::Obligation::new(tcx, cause, wfcx.param_env, trait_ref.without_const()); + let obligation = traits::Obligation::new(tcx, cause, wfcx.param_env, trait_ref); if wfcx.infcx.predicate_must_hold_modulo_regions(&obligation) { true diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 3f0d0a76027f4..3bcf4d7885121 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -805,8 +805,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.param_env, ty::Binder::dummy( self.tcx.at(self.cause.span).mk_trait_ref(hir::LangItem::PointerSized, [a]), - ) - .to_poly_trait_predicate(), + ), )); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index a31ab9c8b23b8..e2cdb2ab3a712 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2157,11 +2157,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, traits::ObligationCause::dummy(), self.param_env, - ty::Binder::dummy(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }), + ty::Binder::dummy(trait_ref), ); match SelectionContext::new(&self).select(&obligation) { Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index b5aa8cd6e7c5b..625cd18422370 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1096,8 +1096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Binder::dummy(self.tcx.mk_trait_ref( into_def_id, [expr_ty, expected_ty] - )) - .to_poly_trait_predicate(), + )), )) { let sugg = if expr.precedence().order() >= PREC_POSTFIX { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 9b1f0cff074a6..9c2de1763b080 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -348,7 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ), self.param_env, - poly_trait_ref.without_const(), + poly_trait_ref, ), substs, ) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 44c3edf06a883..120ef2dac19ed 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1430,7 +1430,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { trait_ref: ty::TraitRef<'tcx>, ) -> traits::SelectionResult<'tcx, traits::Selection<'tcx>> { let cause = traits::ObligationCause::misc(self.span, self.body_id); - let predicate = ty::Binder::dummy(trait_ref).to_poly_trait_predicate(); + let predicate = ty::Binder::dummy(trait_ref); let obligation = traits::Obligation::new(self.tcx, cause, self.param_env, predicate); traits::SelectionContext::new(self).select(&obligation) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0458c4abd3d4c..e8424826891f1 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1152,6 +1152,25 @@ impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Binder<'tcx, PredicateKind<'tc } } +impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Binder<'tcx, TraitRef<'tcx>> { + #[inline(always)] + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + let pred: PolyTraitPredicate<'tcx> = self.to_predicate(tcx); + pred.to_predicate(tcx) + } +} + +impl<'tcx> ToPredicate<'tcx, PolyTraitPredicate<'tcx>> for Binder<'tcx, TraitRef<'tcx>> { + #[inline(always)] + fn to_predicate(self, _: TyCtxt<'tcx>) -> PolyTraitPredicate<'tcx> { + self.map_bound(|trait_ref| TraitPredicate { + trait_ref, + constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, + }) + } +} + impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyTraitPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { self.map_bound(PredicateKind::Trait).to_predicate(tcx) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index e7a751fa0afca..a53b275fb02cd 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -853,23 +853,6 @@ impl<'tcx> PolyTraitRef<'tcx> { pub fn def_id(&self) -> DefId { self.skip_binder().def_id } - - pub fn to_poly_trait_predicate(&self) -> ty::PolyTraitPredicate<'tcx> { - self.map_bound(|trait_ref| ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }) - } - - /// Same as [`PolyTraitRef::to_poly_trait_predicate`] but sets a negative polarity instead. - pub fn to_poly_trait_predicate_negative_polarity(&self) -> ty::PolyTraitPredicate<'tcx> { - self.map_bound(|trait_ref| ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Negative, - }) - } } impl rustc_errors::IntoDiagnosticArg for PolyTraitRef<'_> { diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 2baa3bfcb6401..4ad3343d3031b 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -17,6 +17,7 @@ rustc_index = { path = "../rustc_index" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } rustc_infer = { path = "../rustc_infer" } +rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index fb1ea9ed300ad..3f0f05711edd3 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -1,7 +1,7 @@ use crate::build::ExprCategory; +use crate::errors::*; use rustc_middle::thir::visit::{self, Visitor}; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_middle::mir::BorrowKind; use rustc_middle::thir::*; @@ -12,7 +12,6 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::Symbol; use rustc_span::Span; -use std::borrow::Cow; use std::ops::Bound; struct UnsafetyVisitor<'a, 'tcx> { @@ -46,7 +45,9 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { self.warn_unused_unsafe( hir_id, block_span, - Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")), + Some(UnusedUnsafeEnclosing::Block { + span: self.tcx.sess.source_map().guess_head_span(enclosing_span), + }), ); f(self); } else { @@ -60,7 +61,9 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { hir_id, span, if self.unsafe_op_in_unsafe_fn_allowed() { - self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn")) + self.body_unsafety + .unsafe_fn_sig_span() + .map(|span| UnusedUnsafeEnclosing::Function { span }) } else { None }, @@ -83,30 +86,11 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { } SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {} SafetyContext::UnsafeFn => { - let (description, note) = kind.description_and_note(self.tcx); // unsafe_op_in_unsafe_fn is disallowed - self.tcx.struct_span_lint_hir( - UNSAFE_OP_IN_UNSAFE_FN, - self.hir_context, - span, - format!("{} is unsafe and requires unsafe block (error E0133)", description,), - |lint| lint.span_label(span, kind.simple_description()).note(note), - ) + kind.emit_unsafe_op_in_unsafe_fn_lint(self.tcx, self.hir_context, span); } SafetyContext::Safe => { - let (description, note) = kind.description_and_note(self.tcx); - let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" }; - struct_span_err!( - self.tcx.sess, - span, - E0133, - "{} is unsafe and requires unsafe{} block", - description, - fn_sugg, - ) - .span_label(span, kind.simple_description()) - .note(note) - .emit(); + kind.emit_requires_unsafe_err(self.tcx, span, unsafe_op_in_unsafe_fn_allowed); } } } @@ -115,17 +99,15 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { &self, hir_id: hir::HirId, block_span: Span, - enclosing_unsafe: Option<(Span, &'static str)>, + enclosing_unsafe: Option, ) { let block_span = self.tcx.sess.source_map().guess_head_span(block_span); - let msg = "unnecessary `unsafe` block"; - self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, msg, |lint| { - lint.span_label(block_span, msg); - if let Some((span, kind)) = enclosing_unsafe { - lint.span_label(span, format!("because it's nested under this `unsafe` {}", kind)); - } - lint - }); + self.tcx.emit_spanned_lint( + UNUSED_UNSAFE, + hir_id, + block_span, + UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe }, + ); } /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node. @@ -529,81 +511,189 @@ enum UnsafeOpKind { use UnsafeOpKind::*; impl UnsafeOpKind { - pub fn simple_description(&self) -> &'static str { - match self { - CallToUnsafeFunction(..) => "call to unsafe function", - UseOfInlineAssembly => "use of inline assembly", - InitializingTypeWith => "initializing type with `rustc_layout_scalar_valid_range` attr", - UseOfMutableStatic => "use of mutable static", - UseOfExternStatic => "use of extern static", - DerefOfRawPointer => "dereference of raw pointer", - AccessToUnionField => "access to union field", - MutationOfLayoutConstrainedField => "mutation of layout constrained field", - BorrowOfLayoutConstrainedField => { - "borrow of layout constrained field with interior mutability" - } - CallToFunctionWith(..) => "call to function with `#[target_feature]`", - } - } - - pub fn description_and_note(&self, tcx: TyCtxt<'_>) -> (Cow<'static, str>, &'static str) { + pub fn emit_unsafe_op_in_unsafe_fn_lint( + &self, + tcx: TyCtxt<'_>, + hir_id: hir::HirId, + span: Span, + ) { match self { - CallToUnsafeFunction(did) => ( - if let Some(did) = did { - Cow::from(format!("call to unsafe function `{}`", tcx.def_path_str(*did))) - } else { - Cow::Borrowed(self.simple_description()) + CallToUnsafeFunction(did) if did.is_some() => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe { + span, + function: &tcx.def_path_str(did.unwrap()), }, - "consult the function's documentation for information on how to avoid undefined \ - behavior", ), - UseOfInlineAssembly => ( - Cow::Borrowed(self.simple_description()), - "inline assembly is entirely unchecked and can cause undefined behavior", + CallToUnsafeFunction(..) => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span }, + ), + UseOfInlineAssembly => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span }, ), - InitializingTypeWith => ( - Cow::Borrowed(self.simple_description()), - "initializing a layout restricted type's field with a value outside the valid \ - range is undefined behavior", + InitializingTypeWith => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span }, ), - UseOfMutableStatic => ( - Cow::Borrowed(self.simple_description()), - "mutable statics can be mutated by multiple threads: aliasing violations or data \ - races will cause undefined behavior", + UseOfMutableStatic => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span }, ), - UseOfExternStatic => ( - Cow::Borrowed(self.simple_description()), - "extern statics are not controlled by the Rust type system: invalid data, \ - aliasing violations or data races will cause undefined behavior", + UseOfExternStatic => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span }, ), - DerefOfRawPointer => ( - Cow::Borrowed(self.simple_description()), - "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \ - and cause data races: all of these are undefined behavior", + DerefOfRawPointer => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span }, ), - AccessToUnionField => ( - Cow::Borrowed(self.simple_description()), - "the field may not be properly initialized: using uninitialized data will cause \ - undefined behavior", + AccessToUnionField => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span }, ), - MutationOfLayoutConstrainedField => ( - Cow::Borrowed(self.simple_description()), - "mutating layout constrained fields cannot statically be checked for valid values", + MutationOfLayoutConstrainedField => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span }, ), - BorrowOfLayoutConstrainedField => ( - Cow::Borrowed(self.simple_description()), - "references to fields of layout constrained fields lose the constraints. Coupled \ - with interior mutability, the field can be changed to invalid values", + BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span }, ), - CallToFunctionWith(did) => ( - Cow::from(format!( - "call to function `{}` with `#[target_feature]`", - tcx.def_path_str(*did) - )), - "can only be called if the required target features are available", + CallToFunctionWith(did) => tcx.emit_spanned_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe { + span, + function: &tcx.def_path_str(*did), + }, ), } } + + pub fn emit_requires_unsafe_err( + &self, + tcx: TyCtxt<'_>, + span: Span, + unsafe_op_in_unsafe_fn_allowed: bool, + ) { + match self { + CallToUnsafeFunction(did) if did.is_some() && unsafe_op_in_unsafe_fn_allowed => { + tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + function: &tcx.def_path_str(did.unwrap()), + }); + } + CallToUnsafeFunction(did) if did.is_some() => { + tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafe { + span, + function: &tcx.def_path_str(did.unwrap()), + }); + } + CallToUnsafeFunction(..) if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess.emit_err( + CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { span }, + ); + } + CallToUnsafeFunction(..) => { + tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless { span }); + } + UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + UseOfInlineAssembly => { + tcx.sess.emit_err(UseOfInlineAssemblyRequiresUnsafe { span }); + } + InitializingTypeWith if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + InitializingTypeWith => { + tcx.sess.emit_err(InitializingTypeWithRequiresUnsafe { span }); + } + UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + UseOfMutableStatic => { + tcx.sess.emit_err(UseOfMutableStaticRequiresUnsafe { span }); + } + UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + UseOfExternStatic => { + tcx.sess.emit_err(UseOfExternStaticRequiresUnsafe { span }); + } + DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + DerefOfRawPointer => { + tcx.sess.emit_err(DerefOfRawPointerRequiresUnsafe { span }); + } + AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess + .emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }); + } + AccessToUnionField => { + tcx.sess.emit_err(AccessToUnionFieldRequiresUnsafe { span }); + } + MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess.emit_err( + MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + }, + ); + } + MutationOfLayoutConstrainedField => { + tcx.sess.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe { span }); + } + BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess.emit_err( + BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span }, + ); + } + BorrowOfLayoutConstrainedField => { + tcx.sess.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe { span }); + } + CallToFunctionWith(did) if unsafe_op_in_unsafe_fn_allowed => { + tcx.sess.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + function: &tcx.def_path_str(*did), + }); + } + CallToFunctionWith(did) => { + tcx.sess.emit_err(CallToFunctionWithRequiresUnsafe { + span, + function: &tcx.def_path_str(*did), + }); + } + } + } } pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam) { diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs new file mode 100644 index 0000000000000..01c0b856072b0 --- /dev/null +++ b/compiler/rustc_mir_build/src/errors.rs @@ -0,0 +1,614 @@ +use crate::thir::pattern::MatchCheckCtxt; +use rustc_errors::Handler; +use rustc_errors::{ + error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan, +}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{symbol::Ident, Span}; + +#[derive(LintDiagnostic)] +#[diag(mir_build_unconditional_recursion)] +#[help] +pub struct UnconditionalRecursion { + #[label] + pub span: Span, + #[label(mir_build_unconditional_recursion_call_site_label)] + pub call_sites: Vec, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe<'a> { + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless)] +#[note] +pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe)] +pub struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe)] +#[note] +pub struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe<'a> { + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(Diagnostic)] +#[diag(mir_build_call_to_unsafe_fn_requires_unsafe, code = "E0133")] +#[note] +pub struct CallToUnsafeFunctionRequiresUnsafe<'a> { + #[primary_span] + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(Diagnostic)] +#[diag(mir_build_call_to_unsafe_fn_requires_unsafe_nameless, code = "E0133")] +#[note] +pub struct CallToUnsafeFunctionRequiresUnsafeNameless { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed<'a> { + #[primary_span] + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(Diagnostic)] +#[diag( + mir_build_call_to_unsafe_fn_requires_unsafe_nameless_unsafe_op_in_unsafe_fn_allowed, + code = "E0133" +)] +#[note] +pub struct CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_inline_assembly_requires_unsafe, code = "E0133")] +#[note] +pub struct UseOfInlineAssemblyRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_initializing_type_with_requires_unsafe, code = "E0133")] +#[note] +pub struct InitializingTypeWithRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag( + mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = "E0133" +)] +#[note] +pub struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_mutable_static_requires_unsafe, code = "E0133")] +#[note] +pub struct UseOfMutableStaticRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_mutable_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_extern_static_requires_unsafe, code = "E0133")] +#[note] +pub struct UseOfExternStaticRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_deref_raw_pointer_requires_unsafe, code = "E0133")] +#[note] +pub struct DerefOfRawPointerRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_union_field_requires_unsafe, code = "E0133")] +#[note] +pub struct AccessToUnionFieldRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_mutation_of_layout_constrained_field_requires_unsafe, code = "E0133")] +#[note] +pub struct MutationOfLayoutConstrainedFieldRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag( + mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = "E0133" +)] +#[note] +pub struct MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_borrow_of_layout_constrained_field_requires_unsafe, code = "E0133")] +#[note] +pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafe { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag( + mir_build_borrow_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = "E0133" +)] +#[note] +pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_call_to_fn_with_requires_unsafe, code = "E0133")] +#[note] +pub struct CallToFunctionWithRequiresUnsafe<'a> { + #[primary_span] + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(Diagnostic)] +#[diag(mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")] +#[note] +pub struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed<'a> { + #[primary_span] + #[label] + pub span: Span, + pub function: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unused_unsafe)] +pub struct UnusedUnsafe { + #[label] + pub span: Span, + #[subdiagnostic] + pub enclosing: Option, +} + +#[derive(Subdiagnostic)] +pub enum UnusedUnsafeEnclosing { + #[label(mir_build_unused_unsafe_enclosing_block_label)] + Block { + #[primary_span] + span: Span, + }, + #[label(mir_build_unused_unsafe_enclosing_fn_label)] + Function { + #[primary_span] + span: Span, + }, +} + +pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> { + pub cx: &'m MatchCheckCtxt<'p, 'tcx>, + pub expr_span: Span, + pub span: Span, + pub ty: Ty<'tcx>, +} + +impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> { + fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_span_err_with_code( + self.span, + rustc_errors::fluent::mir_build_non_exhaustive_patterns_type_not_empty, + error_code!(E0004), + ); + + let peeled_ty = self.ty.peel_refs(); + diag.set_arg("ty", self.ty); + diag.set_arg("peeled_ty", peeled_ty); + + if let ty::Adt(def, _) = peeled_ty.kind() { + let def_span = self + .cx + .tcx + .hir() + .get_if_local(def.did()) + .and_then(|node| node.ident()) + .map(|ident| ident.span) + .unwrap_or_else(|| self.cx.tcx.def_span(def.did())); + + // workaround to make test pass + let mut span: MultiSpan = def_span.into(); + span.push_span_label(def_span, ""); + + diag.span_note(span, rustc_errors::fluent::def_note); + } + + let is_variant_list_non_exhaustive = match self.ty.kind() { + ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local() => { + true + } + _ => false, + }; + + if is_variant_list_non_exhaustive { + diag.note(rustc_errors::fluent::non_exhaustive_type_note); + } else { + diag.note(rustc_errors::fluent::type_note); + } + + if let ty::Ref(_, sub_ty, _) = self.ty.kind() { + if self.cx.tcx.is_ty_uninhabited_from(self.cx.module, *sub_ty, self.cx.param_env) { + diag.note(rustc_errors::fluent::reference_note); + } + } + + let mut suggestion = None; + let sm = self.cx.tcx.sess.source_map(); + if self.span.eq_ctxt(self.expr_span) { + // Get the span for the empty match body `{}`. + let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) { + (format!("\n{}", snippet), " ") + } else { + (" ".to_string(), "") + }; + suggestion = Some(( + self.span.shrink_to_hi().with_hi(self.expr_span.hi()), + format!( + " {{{indentation}{more}_ => todo!(),{indentation}}}", + indentation = indentation, + more = more, + ), + )); + } + + if let Some((span, sugg)) = suggestion { + diag.span_suggestion_verbose( + span, + rustc_errors::fluent::suggestion, + sugg, + Applicability::HasPlaceholders, + ); + } else { + diag.help(rustc_errors::fluent::help); + } + + diag + } +} + +#[derive(Diagnostic)] +#[diag(mir_build_static_in_pattern, code = "E0158")] +pub struct StaticInPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_assoc_const_in_pattern, code = "E0158")] +pub struct AssocConstInPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_const_param_in_pattern, code = "E0158")] +pub struct ConstParamInPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_non_const_path, code = "E0080")] +pub struct NonConstPath { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unreachable_pattern)] +pub struct UnreachablePattern { + #[label] + pub span: Option, + #[label(catchall_label)] + pub catchall: Option, +} + +#[derive(Diagnostic)] +#[diag(mir_build_const_pattern_depends_on_generic_parameter)] +pub struct ConstPatternDependsOnGenericParameter { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_could_not_eval_const_pattern)] +pub struct CouldNotEvalConstPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper, code = "E0030")] +pub struct LowerRangeBoundMustBeLessThanOrEqualToUpper { + #[primary_span] + #[label] + pub span: Span, + #[note(teach_note)] + pub teach: Option<()>, +} + +#[derive(Diagnostic)] +#[diag(mir_build_lower_range_bound_must_be_less_than_upper, code = "E0579")] +pub struct LowerRangeBoundMustBeLessThanUpper { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_leading_irrefutable_let_patterns)] +#[note] +#[help] +pub struct LeadingIrrefutableLetPatterns { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_trailing_irrefutable_let_patterns)] +#[note] +#[help] +pub struct TrailingIrrefutableLetPatterns { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_bindings_with_variant_name, code = "E0170")] +pub struct BindingsWithVariantName { + #[suggestion(code = "{ty_path}::{ident}", applicability = "machine-applicable")] + pub suggestion: Option, + pub ty_path: String, + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_irrefutable_let_patterns_generic_let)] +#[note] +#[help] +pub struct IrrefutableLetPatternsGenericLet { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_irrefutable_let_patterns_if_let)] +#[note] +#[help] +pub struct IrrefutableLetPatternsIfLet { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_irrefutable_let_patterns_if_let_guard)] +#[note] +#[help] +pub struct IrrefutableLetPatternsIfLetGuard { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_irrefutable_let_patterns_let_else)] +#[note] +#[help] +pub struct IrrefutableLetPatternsLetElse { + pub count: usize, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_irrefutable_let_patterns_while_let)] +#[note] +#[help] +pub struct IrrefutableLetPatternsWhileLet { + pub count: usize, +} + +#[derive(Diagnostic)] +#[diag(mir_build_borrow_of_moved_value)] +pub struct BorrowOfMovedValue<'tcx> { + #[primary_span] + pub span: Span, + #[label] + #[label(occurs_because_label)] + pub binding_span: Span, + #[label(value_borrowed_label)] + pub conflicts_ref: Vec, + pub name: Ident, + pub ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(mir_build_multiple_mut_borrows)] +pub struct MultipleMutBorrows { + #[primary_span] + pub span: Span, + #[label] + pub binding_span: Span, + #[subdiagnostic(eager)] + pub occurences: Vec, + pub name: Ident, +} + +#[derive(Subdiagnostic)] +pub enum MultipleMutBorrowOccurence { + #[label(mutable_borrow)] + Mutable { + #[primary_span] + span: Span, + name_mut: Ident, + }, + #[label(immutable_borrow)] + Immutable { + #[primary_span] + span: Span, + name_immut: Ident, + }, + #[label(moved)] + Moved { + #[primary_span] + span: Span, + name_moved: Ident, + }, +} diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 8797529459534..2b05e92fdcf20 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -19,6 +19,7 @@ extern crate rustc_middle; mod build; mod check_unsafety; +mod errors; mod lints; pub mod thir; diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index b21f30efce807..8529c64cd5cca 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -1,3 +1,4 @@ +use crate::errors::UnconditionalRecursion; use rustc_data_structures::graph::iterate::{ NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, }; @@ -36,19 +37,11 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let sp = tcx.def_span(def_id); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( UNCONDITIONAL_RECURSION, hir_id, sp, - "function cannot return without recursing", - |lint| { - lint.span_label(sp, "cannot return without recursing"); - // offer some help to the programmer. - for call_span in vis.reachable_recursive_calls { - lint.span_label(call_span, "recursive call site"); - } - lint.help("a `loop` may express intention better if this is on purpose") - }, + UnconditionalRecursion { span: sp, call_sites: vis.reachable_recursive_calls }, ); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index ba04cb6eef82d..1317574050024 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -4,11 +4,13 @@ use super::usefulness::{ }; use super::{PatCtxt, PatternError}; +use crate::errors::*; + use rustc_arena::TypedArena; use rustc_ast::Mutability; use rustc_errors::{ - error_code, pluralize, struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, MultiSpan, + pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::*; @@ -107,28 +109,20 @@ impl PatCtxt<'_, '_> { for error in &self.errors { match *error { PatternError::StaticInPattern(span) => { - self.span_e0158(span, "statics cannot be referenced in patterns") + self.tcx.sess.emit_err(StaticInPattern { span }); } PatternError::AssocConstInPattern(span) => { - self.span_e0158(span, "associated consts cannot be referenced in patterns") + self.tcx.sess.emit_err(AssocConstInPattern { span }); } PatternError::ConstParamInPattern(span) => { - self.span_e0158(span, "const parameters cannot be referenced in patterns") + self.tcx.sess.emit_err(ConstParamInPattern { span }); } PatternError::NonConstPath(span) => { - rustc_middle::mir::interpret::struct_error( - self.tcx.at(span), - "runtime values cannot be referenced in patterns", - ) - .emit(); + self.tcx.sess.emit_err(NonConstPath { span }); } } } } - - fn span_e0158(&self, span: Span, text: &str) { - struct_span_err!(self.tcx.sess, span, E0158, "{}", text).emit(); - } } impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { @@ -345,29 +339,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { ); return true; } - let lint_affix = |affix: &[Option<(Span, bool)>], kind, suggestion| { - let span_start = affix[0].unwrap().0; - let span_end = affix.last().unwrap().unwrap().0; - let span = span_start.to(span_end); - let cnt = affix.len(); - let s = pluralize!(cnt); - cx.tcx.struct_span_lint_hir( - IRREFUTABLE_LET_PATTERNS, - top, - span, - format!("{kind} irrefutable pattern{s} in let chain"), - |lint| { - lint.note(format!( - "{these} pattern{s} will always match", - these = pluralize!("this", cnt), - )) - .help(format!( - "consider moving {} {suggestion}", - if cnt > 1 { "them" } else { "it" } - )) - }, - ); - }; if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 { // The chain has a non-zero prefix of irrefutable `let` statements. @@ -381,13 +352,21 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) { // Emit the lint let prefix = &chain_refutabilities[..until]; - lint_affix(prefix, "leading", "outside of the construct"); + let span_start = prefix[0].unwrap().0; + let span_end = prefix.last().unwrap().unwrap().0; + let span = span_start.to(span_end); + let count = prefix.len(); + cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, LeadingIrrefutableLetPatterns { count }); } } if let Some(from) = chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false)))) && from != (chain_refutabilities.len() - 1) { // The chain has a non-empty suffix of irrefutable `let` statements let suffix = &chain_refutabilities[from + 1..]; - lint_affix(suffix, "trailing", "into the body"); + let span_start = suffix[0].unwrap().0; + let span_end = suffix.last().unwrap().unwrap().0; + let span = span_start.to(span_end); + let count = suffix.len(); + cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, TrailingIrrefutableLetPatterns { count }); } true } @@ -568,32 +547,20 @@ fn check_for_bindings_named_same_as_variants( }) { let variant_count = edef.variants().len(); - cx.tcx.struct_span_lint_hir( + let ty_path = cx.tcx.def_path_str(edef.did()); + cx.tcx.emit_spanned_lint( BINDINGS_WITH_VARIANT_NAME, p.hir_id, p.span, - DelayDm(|| format!( - "pattern binding `{}` is named the same as one \ - of the variants of the type `{}`", - ident, cx.tcx.def_path_str(edef.did()) - )), - |lint| { - let ty_path = cx.tcx.def_path_str(edef.did()); - lint.code(error_code!(E0170)); - + BindingsWithVariantName { // If this is an irrefutable pattern, and there's > 1 variant, // then we can't actually match on this. Applying the below // suggestion would produce code that breaks on `check_irrefutable`. - if rf == Refutable || variant_count == 1 { - lint.span_suggestion( - p.span, - "to match on the variant, qualify the path", - format!("{}::{}", ty_path, ident), - Applicability::MachineApplicable, - ); - } - - lint + suggestion: if rf == Refutable || variant_count == 1 { + Some(p.span) + } else { None }, + ty_path, + ident, }, ) } @@ -611,14 +578,12 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { } fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option) { - tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern", |lint| { - if let Some(catchall) = catchall { - // We had a catchall pattern, hint at that. - lint.span_label(span, "unreachable pattern"); - lint.span_label(catchall, "matches any value"); - } - lint - }); + tcx.emit_spanned_lint( + UNREACHABLE_PATTERNS, + id, + span, + UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall }, + ); } fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) { @@ -634,67 +599,18 @@ fn irrefutable_let_patterns( span: Span, ) { macro_rules! emit_diag { - ( - $lint:expr, - $source_name:expr, - $note_sufix:expr, - $help_sufix:expr - ) => {{ - let s = pluralize!(count); - let these = pluralize!("this", count); - tcx.struct_span_lint_hir( - IRREFUTABLE_LET_PATTERNS, - id, - span, - format!("irrefutable {} pattern{s}", $source_name), - |lint| { - lint.note(&format!( - "{these} pattern{s} will always match, so the {}", - $note_sufix - )) - .help(concat!("consider ", $help_sufix)) - }, - ) + ($lint:tt) => {{ + tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count }); }}; } match source { - LetSource::GenericLet => { - emit_diag!(lint, "`let`", "`let` is useless", "removing `let`"); - } - LetSource::IfLet => { - emit_diag!( - lint, - "`if let`", - "`if let` is useless", - "replacing the `if let` with a `let`" - ); - } - LetSource::IfLetGuard => { - emit_diag!( - lint, - "`if let` guard", - "guard is useless", - "removing the guard and adding a `let` inside the match arm" - ); - } - LetSource::LetElse => { - emit_diag!( - lint, - "`let...else`", - "`else` clause is useless", - "removing the `else` clause" - ); - } - LetSource::WhileLet => { - emit_diag!( - lint, - "`while let`", - "loop will never exit", - "instead using a `loop { ... }` with a `let` inside it" - ); - } - }; + LetSource::GenericLet => emit_diag!(IrrefutableLetPatternsGenericLet), + LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet), + LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard), + LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse), + LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet), + } } fn is_let_irrefutable<'p, 'tcx>( @@ -760,15 +676,17 @@ fn non_exhaustive_match<'p, 'tcx>( // informative. let mut err; let pattern; - let mut patterns_len = 0; + let patterns_len; if is_empty_match && !non_empty_enum { - err = create_e0004( - cx.tcx.sess, - sp, - format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty), - ); - pattern = "_".to_string(); + cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty { + cx, + expr_span, + span: sp, + ty: scrut_ty, + }); + return; } else { + // FIXME: migration of this diagnostic will require list support let joined_patterns = joined_uncovered_patterns(cx, &witnesses); err = create_e0004( cx.tcx.sess, @@ -1039,16 +957,13 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa } }); if !conflicts_ref.is_empty() { - let occurs_because = format!( - "move occurs because `{}` has type `{}` which does not implement the `Copy` trait", + sess.emit_err(BorrowOfMovedValue { + span: pat.span, + binding_span, + conflicts_ref, name, - typeck_results.node_type(pat.hir_id), - ); - sess.struct_span_err(pat.span, "borrow of moved value") - .span_label(binding_span, format!("value moved into `{}` here", name)) - .span_label(binding_span, occurs_because) - .span_labels(conflicts_ref, "value borrowed here after move") - .emit(); + ty: typeck_results.node_type(pat.hir_id), + }); } return; } @@ -1078,19 +993,18 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa // Report errors if any. if !conflicts_mut_mut.is_empty() { // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`. - let mut err = sess - .struct_span_err(pat.span, "cannot borrow value as mutable more than once at a time"); - err.span_label(binding_span, format!("first mutable borrow, by `{}`, occurs here", name)); - for (span, name) in conflicts_mut_mut { - err.span_label(span, format!("another mutable borrow, by `{}`, occurs here", name)); + let mut occurences = vec![]; + + for (span, name_mut) in conflicts_mut_mut { + occurences.push(MultipleMutBorrowOccurence::Mutable { span, name_mut }); } - for (span, name) in conflicts_mut_ref { - err.span_label(span, format!("also borrowed as immutable, by `{}`, here", name)); + for (span, name_immut) in conflicts_mut_ref { + occurences.push(MultipleMutBorrowOccurence::Immutable { span, name_immut }); } - for (span, name) in conflicts_move { - err.span_label(span, format!("also moved into `{}` here", name)); + for (span, name_moved) in conflicts_move { + occurences.push(MultipleMutBorrowOccurence::Moved { span, name_moved }); } - err.emit(); + sess.emit_err(MultipleMutBorrows { span: pat.span, binding_span, occurences, name }); } else if !conflicts_mut_ref.is_empty() { // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse. let (primary, also) = match mut_outer { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 4b6608faba6f1..7e4ae6766e293 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -6,10 +6,12 @@ mod deconstruct_pat; mod usefulness; pub(crate) use self::check_match::check_match; +pub(crate) use self::usefulness::MatchCheckCtxt; +use crate::errors::*; use crate::thir::util::UserAnnotatedTyHelpers; -use rustc_errors::struct_span_err; +use rustc_errors::error_code; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; @@ -139,13 +141,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } // `x..y` where `x >= y`. The range is empty => error. (RangeEnd::Excluded, _) => { - struct_span_err!( - self.tcx.sess, - span, - E0579, - "lower range bound must be less than upper" - ) - .emit(); + self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span }); PatKind::Wild } // `x..=y` where `x == y`. @@ -156,23 +152,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } // `x..=y` where `x > y` hence the range is empty => error. (RangeEnd::Included, _) => { - let mut err = struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper { span, - E0030, - "lower range bound must be less than or equal to upper" - ); - err.span_label(span, "lower bound larger than upper bound"); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "When matching against a range, the compiler \ - verifies that the range is non-empty. Range \ - patterns include both end-points, so this is \ - equivalent to requiring the start of the range \ - to be less than or equal to the end of the range.", - ); - } - err.emit(); + teach: if self.tcx.sess.teach(&error_code!(E0030)) { Some(()) } else { None }, + }); PatKind::Wild } } @@ -501,7 +484,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } Err(_) => { - self.tcx.sess.span_err(span, "could not evaluate constant pattern"); + self.tcx.sess.emit_err(CouldNotEvalConstPattern { span }); return pat_from_kind(PatKind::Wild); } }; @@ -548,11 +531,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { Err(ErrorHandled::TooGeneric) => { // While `Reported | Linted` cases will have diagnostics emitted already // it is not true for TooGeneric case, so we need to give user more information. - self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter"); + self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span }); pat_from_kind(PatKind::Wild) } Err(_) => { - self.tcx.sess.span_err(span, "could not evaluate constant pattern"); + self.tcx.sess.emit_err(CouldNotEvalConstPattern { span }); pat_from_kind(PatKind::Wild) } } @@ -584,7 +567,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind, mir::ConstantKind::Unevaluated(..) => { // If we land here it means the const can't be evaluated because it's `TooGeneric`. - self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter"); + self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span }); return PatKind::Wild; } } diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs index 9b39a9401149a..f5d276d3fc5d3 100644 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ b/compiler/rustc_trait_selection/src/autoderef.rs @@ -130,7 +130,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { tcx, cause.clone(), self.param_env, - ty::Binder::dummy(trait_ref).without_const(), + ty::Binder::dummy(trait_ref), ); if !self.infcx.predicate_may_hold(&obligation) { debug!("overloaded_deref_ty: cannot match obligation"); diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 78af187cd8f80..66f80e92eb771 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -10,7 +10,7 @@ use crate::traits::project::ProjectAndUnifyResult; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{PolyTraitRef, Region, RegionVid}; +use rustc_middle::ty::{ImplPolarity, Region, RegionVid}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; @@ -88,19 +88,22 @@ impl<'tcx> AutoTraitFinder<'tcx> { let trait_ref = tcx.mk_trait_ref(trait_did, [ty]); - let trait_pred = ty::Binder::dummy(trait_ref); - let infcx = tcx.infer_ctxt().build(); let mut selcx = SelectionContext::new(&infcx); - for f in [ - PolyTraitRef::to_poly_trait_predicate, - PolyTraitRef::to_poly_trait_predicate_negative_polarity, - ] { + for polarity in [true, false] { let result = selcx.select(&Obligation::new( tcx, ObligationCause::dummy(), orig_env, - f(&trait_pred), + ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + constness: ty::BoundConstness::NotConst, + polarity: if polarity { + ImplPolarity::Positive + } else { + ImplPolarity::Negative + }, + }), )); if let Ok(Some(ImplSource::UserDefined(_))) = result { debug!( diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index ca4299f7db394..61743d78e9e80 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -39,8 +39,7 @@ pub fn codegen_select_candidate<'tcx>( let mut selcx = SelectionContext::new(&infcx); let obligation_cause = ObligationCause::dummy(); - let obligation = - Obligation::new(tcx, obligation_cause, param_env, trait_ref.to_poly_trait_predicate()); + let obligation = Obligation::new(tcx, obligation_cause, param_env, trait_ref); let selection = match selcx.select(&obligation) { Ok(Some(selection)) => selection, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 59d017545c032..54b135c1dce86 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2111,7 +2111,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) }; - let obligation = obligation.with(self.tcx, trait_ref.to_poly_trait_predicate()); + let obligation = obligation.with(self.tcx, trait_ref); let mut selcx = SelectionContext::with_query_mode( &self, crate::traits::TraitQueryMode::Standard, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 548ca1c1d7faa..4ab43c4e0b067 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -477,7 +477,7 @@ fn subst_and_check_impossible_predicates<'tcx>( // associated items. if let Some(trait_def_id) = tcx.trait_of_item(key.0) { let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1); - predicates.push(ty::Binder::dummy(trait_ref).to_poly_trait_predicate().to_predicate(tcx)); + predicates.push(ty::Binder::dummy(trait_ref).to_predicate(tcx)); } predicates.retain(|predicate| !predicate.needs_subst()); diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 7c4c58ba36167..5bed10dec09f8 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -723,8 +723,7 @@ fn receiver_is_dispatchable<'tcx>( let obligation = { let predicate = ty::Binder::dummy( tcx.mk_trait_ref(dispatch_from_dyn_did, [receiver_ty, unsized_receiver_ty]), - ) - .without_const(); + ); Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate) }; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 9f19b0092c00f..5441a348685e1 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1343,8 +1343,7 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id)); // FIXME(named-returns): Binders let trait_predicate = - ty::Binder::dummy(ty::TraitRef { def_id: trait_def_id, substs: trait_substs }) - .to_poly_trait_predicate(); + ty::Binder::dummy(ty::TraitRef { def_id: trait_def_id, substs: trait_substs }); let _ = selcx.infcx().commit_if_ok(|_| { match selcx.select(&obligation.with(tcx, trait_predicate)) { @@ -1542,7 +1541,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // If we are resolving `>::Item == Type`, // start out by selecting the predicate `T as TraitRef<...>`: let poly_trait_ref = ty::Binder::dummy(obligation.predicate.trait_ref(selcx.tcx())); - let trait_obligation = obligation.with(selcx.tcx(), poly_trait_ref.to_poly_trait_predicate()); + let trait_obligation = obligation.with(selcx.tcx(), poly_trait_ref); let _ = selcx.infcx().commit_if_ok(|_| { let impl_source = match selcx.select(&trait_obligation) { Ok(Some(impl_source)) => impl_source, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 3b107d9570f14..8bb4599544454 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -715,12 +715,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // let trait_ref = tcx.mk_trait_ref(tcx.lang_items().deref_trait()?, [ty]); - let obligation = traits::Obligation::new( - tcx, - cause.clone(), - param_env, - ty::Binder::dummy(trait_ref).without_const(), - ); + let obligation = + traits::Obligation::new(tcx, cause.clone(), param_env, ty::Binder::dummy(trait_ref)); if !self.infcx.predicate_may_hold(&obligation) { return None; } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 2ec5d925b6900..d503d4d8563a9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -634,12 +634,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); let tr = ty::Binder::dummy(self.tcx().at(cause.span).mk_trait_ref(LangItem::Sized, [output_ty])); - nested.push(Obligation::new( - self.infcx.tcx, - cause, - obligation.param_env, - tr.to_poly_trait_predicate(), - )); + nested.push(Obligation::new(self.infcx.tcx, cause, obligation.param_env, tr)); Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested }) } diff --git a/library/alloc/tests/boxed.rs b/library/alloc/tests/boxed.rs index 9e5123be98990..af49826ff30a3 100644 --- a/library/alloc/tests/boxed.rs +++ b/library/alloc/tests/boxed.rs @@ -102,8 +102,18 @@ unsafe impl const Allocator for ConstAllocator { let new_ptr = self.allocate(new_layout)?; if new_layout.size() > 0 { - new_ptr.as_mut_ptr().copy_from_nonoverlapping(ptr.as_ptr(), old_layout.size()); - self.deallocate(ptr, old_layout); + // Safety: `new_ptr` is valid for writes and `ptr` for reads of + // `old_layout.size()`, because `new_layout.size() >= + // old_layout.size()` (which is an invariant that must be upheld by + // callers). + unsafe { + new_ptr.as_mut_ptr().copy_from_nonoverlapping(ptr.as_ptr(), old_layout.size()); + } + // Safety: `ptr` is never used again is also an invariant which must + // be upheld by callers. + unsafe { + self.deallocate(ptr, old_layout); + } } Ok(new_ptr) } @@ -114,12 +124,21 @@ unsafe impl const Allocator for ConstAllocator { old_layout: Layout, new_layout: Layout, ) -> Result, AllocError> { - let new_ptr = self.grow(ptr, old_layout, new_layout)?; + // Safety: Invariants of `grow_zeroed` and `grow` are the same, and must + // be enforced by callers. + let new_ptr = unsafe { self.grow(ptr, old_layout, new_layout)? }; if new_layout.size() > 0 { let old_size = old_layout.size(); let new_size = new_layout.size(); let raw_ptr = new_ptr.as_mut_ptr(); - raw_ptr.add(old_size).write_bytes(0, new_size - old_size); + // Safety: + // - `grow` returned Ok, so the returned pointer must be valid for + // `new_size` bytes + // - `new_size` must be larger than `old_size`, which is an + // invariant which must be upheld by callers. + unsafe { + raw_ptr.add(old_size).write_bytes(0, new_size - old_size); + } } Ok(new_ptr) } @@ -137,8 +156,18 @@ unsafe impl const Allocator for ConstAllocator { let new_ptr = self.allocate(new_layout)?; if new_layout.size() > 0 { - new_ptr.as_mut_ptr().copy_from_nonoverlapping(ptr.as_ptr(), new_layout.size()); - self.deallocate(ptr, old_layout); + // Safety: `new_ptr` and `ptr` are valid for reads/writes of + // `new_layout.size()` because of the invariants of shrink, which + // include `new_layout.size()` being smaller than (or equal to) + // `old_layout.size()`. + unsafe { + new_ptr.as_mut_ptr().copy_from_nonoverlapping(ptr.as_ptr(), new_layout.size()); + } + // Safety: `ptr` is never used again is also an invariant which must + // be upheld by callers. + unsafe { + self.deallocate(ptr, old_layout); + } } Ok(new_ptr) } diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index d066ec03ee57e..d6d2b055b2395 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -48,6 +48,7 @@ #![feature(once_cell)] #![feature(drain_keep_rest)] #![deny(fuzzy_provenance_casts)] +#![deny(unsafe_op_in_unsafe_fn)] use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index e027118704478..7ebed0d5ca699 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1089,7 +1089,8 @@ fn test_into_iter_drop_allocator() { } unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { - System.deallocate(ptr, layout) + // Safety: Invariants passed to caller. + unsafe { System.deallocate(ptr, layout) } } } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index cb0b8d4a9bc19..a1145b90d6580 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -67,12 +67,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { .instantiate(cx.tcx, impl_substs) .predicates .into_iter() - .chain(Some( - ty::Binder::dummy(impl_trait_ref) - .to_poly_trait_predicate() - .map_bound(ty::PredicateKind::Trait) - .to_predicate(infcx.tcx), - )); + .chain(Some(ty::Binder::dummy(impl_trait_ref).to_predicate(infcx.tcx))); for predicate in predicates { debug!("testing predicate {:?}", predicate); let obligation = traits::Obligation::new( diff --git a/src/test/codegen/auxiliary/extern_decl.rs b/src/test/codegen/auxiliary/extern_decl.rs new file mode 100644 index 0000000000000..edc48351869a6 --- /dev/null +++ b/src/test/codegen/auxiliary/extern_decl.rs @@ -0,0 +1,11 @@ +// Auxiliary crate that exports a function and static. Both always +// evaluate to `71`. We force mutability on the static to prevent +// it from being inlined as constant. + +#![crate_type = "lib"] + +#[no_mangle] +pub fn extern_fn() -> u8 { unsafe { extern_static } } + +#[no_mangle] +pub static mut extern_static: u8 = 71; diff --git a/src/test/codegen/static-relocation-model.rs b/src/test/codegen/static-relocation-model.rs new file mode 100644 index 0000000000000..12a7657fa7de1 --- /dev/null +++ b/src/test/codegen/static-relocation-model.rs @@ -0,0 +1,25 @@ +// Verify linkage of external symbols in the static relocation model. +// +// compile-flags: -O -C relocation-model=static +// aux-build: extern_decl.rs + +#![crate_type = "rlib"] + +extern crate extern_decl; + +// The `extern_decl` definitions are imported from a statically linked rust +// crate, thus they are expected to be marked `dso_local` without `dllimport`. +// +// The `access_extern()` symbol is from this compilation unit, thus we expect +// it to be marked `dso_local` as well, given the static relocation model. +// +// CHECK: @extern_static = external dso_local local_unnamed_addr global i8 +// CHECK: define dso_local i8 @access_extern() {{.*}} +// CHECK: declare dso_local i8 @extern_fn() {{.*}} + +#[no_mangle] +pub fn access_extern() -> u8 { + unsafe { + extern_decl::extern_fn() + extern_decl::extern_static + } +} diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 5420a0e782ea8..8c4cff66f554b 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -698,9 +698,8 @@ fn matches_preds<'tcx>( cx.tcx, ObligationCause::dummy(), cx.param_env, - cx.tcx.mk_predicate(Binder::bind_with_vars( + cx.tcx.mk_predicate(Binder::dummy( PredicateKind::Projection(p.with_self_ty(cx.tcx, ty)), - List::empty(), )), )), ExistentialPredicate::AutoTrait(p) => infcx diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 8931c17bbdc16..41c5d36671de0 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -221,6 +221,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "command-group" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7a8a86f409b4a59df3a3e4bee2de0b83f1755fdd2a25e3a9684c396fc4bed2c" +dependencies = [ + "nix", + "winapi", +] + [[package]] name = "countme" version = "3.0.1" @@ -300,7 +310,7 @@ dependencies = [ "hashbrown", "lock_api", "once_cell", - "parking_lot_core 0.9.3", + "parking_lot_core 0.9.4", ] [[package]] @@ -359,14 +369,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] @@ -390,6 +400,7 @@ name = "flycheck" version = "0.0.0" dependencies = [ "cargo_metadata", + "command-group", "crossbeam-channel", "jod-thread", "paths", @@ -963,11 +974,24 @@ dependencies = [ [[package]] name = "miow" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7377f7792b3afb6a3cba68daa54ca23c032137010460d667fda53a8d66be00e" +checksum = "52ffbca2f655e33c08be35d87278e5b18b89550a37dbd598c20db92f6a471123" dependencies = [ - "windows-sys 0.28.0", + "windows-sys 0.42.0", +] + +[[package]] +name = "nix" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", ] [[package]] @@ -1037,7 +1061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.3", + "parking_lot_core 0.9.4", ] [[package]] @@ -1056,15 +1080,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] @@ -1979,19 +2003,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6" -dependencies = [ - "windows_aarch64_msvc 0.28.0", - "windows_i686_gnu 0.28.0", - "windows_i686_msvc 0.28.0", - "windows_x86_64_gnu 0.28.0", - "windows_x86_64_msvc 0.28.0", -] - [[package]] name = "windows-sys" version = "0.36.1" @@ -2006,10 +2017,25 @@ dependencies = [ ] [[package]] -name = "windows_aarch64_msvc" -version = "0.28.0" +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" [[package]] name = "windows_aarch64_msvc" @@ -2018,10 +2044,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] -name = "windows_i686_gnu" -version = "0.28.0" +name = "windows_aarch64_msvc" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" [[package]] name = "windows_i686_gnu" @@ -2030,10 +2056,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] -name = "windows_i686_msvc" -version = "0.28.0" +name = "windows_i686_gnu" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" [[package]] name = "windows_i686_msvc" @@ -2042,10 +2068,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] -name = "windows_x86_64_gnu" -version = "0.28.0" +name = "windows_i686_msvc" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" [[package]] name = "windows_x86_64_gnu" @@ -2054,10 +2080,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] -name = "windows_x86_64_msvc" -version = "0.28.0" +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" [[package]] name = "windows_x86_64_msvc" @@ -2065,6 +2097,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + [[package]] name = "write-json" version = "0.1.2" diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index f02a51ab6c47f..a484ecec68250 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index ee1ad677a95f2..2857420c285a7 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml index 2ad32d24837d7..514d567fcce75 100644 --- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml +++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false @@ -17,6 +17,7 @@ rustc-hash = "1.1.0" serde = { version = "1.0.137", features = ["derive"] } serde_json = "1.0.86" jod-thread = "0.1.2" +command-group = "1.0.8" toolchain = { path = "../toolchain", version = "0.0.0" } stdx = { path = "../stdx", version = "0.0.0" } diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs index 8a91d6066614f..8f93dad06e3f5 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs @@ -10,11 +10,12 @@ use std::{ time::Duration, }; +use command_group::{CommandGroup, GroupChild}; use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; use paths::AbsPathBuf; use rustc_hash::FxHashMap; use serde::Deserialize; -use stdx::{process::streaming_output, JodChild}; +use stdx::process::streaming_output; pub use cargo_metadata::diagnostic::{ Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, @@ -39,7 +40,7 @@ pub enum InvocationLocation { pub enum FlycheckConfig { CargoCommand { command: String, - target_triple: Option, + target_triples: Vec, all_targets: bool, no_default_features: bool, all_features: bool, @@ -285,7 +286,7 @@ impl FlycheckActor { let (mut cmd, args) = match &self.config { FlycheckConfig::CargoCommand { command, - target_triple, + target_triples, no_default_features, all_targets, all_features, @@ -299,7 +300,7 @@ impl FlycheckActor { cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) .arg(self.root.join("Cargo.toml").as_os_str()); - if let Some(target) = target_triple { + for target in target_triples { cmd.args(&["--target", target.as_str()]); } if *all_targets { @@ -359,10 +360,12 @@ impl FlycheckActor { } } +struct JodChild(GroupChild); + /// A handle to a cargo process used for fly-checking. struct CargoHandle { /// The handle to the actual cargo process. As we cannot cancel directly from with - /// a read syscall dropping and therefor terminating the process is our best option. + /// a read syscall dropping and therefore terminating the process is our best option. child: JodChild, thread: jod_thread::JoinHandle>, receiver: Receiver, @@ -371,10 +374,10 @@ struct CargoHandle { impl CargoHandle { fn spawn(mut command: Command) -> std::io::Result { command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); - let mut child = JodChild::spawn(command)?; + let mut child = command.group_spawn().map(JodChild)?; - let stdout = child.stdout.take().unwrap(); - let stderr = child.stderr.take().unwrap(); + let stdout = child.0.inner().stdout.take().unwrap(); + let stderr = child.0.inner().stderr.take().unwrap(); let (sender, receiver) = unbounded(); let actor = CargoActor::new(sender, stdout, stderr); @@ -386,13 +389,13 @@ impl CargoHandle { } fn cancel(mut self) { - let _ = self.child.kill(); - let _ = self.child.wait(); + let _ = self.child.0.kill(); + let _ = self.child.0.wait(); } fn join(mut self) -> io::Result<()> { - let _ = self.child.kill(); - let exit_status = self.child.wait()?; + let _ = self.child.0.kill(); + let exit_status = self.child.0.wait()?; let (read_at_least_one_message, error) = self.thread.join()?; if read_at_least_one_message || exit_status.success() { Ok(()) diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 4ad8e75970b59..22f98ea7cd450 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index 2dc69b00ace00..9c76969086485 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -236,11 +236,19 @@ impl TraitData { .by_key("rustc_skip_array_during_method_dispatch") .exists(); - let mut collector = - AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr)); - collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items); - let (items, attribute_calls, diagnostics) = collector.finish(); - + let (items, attribute_calls, diagnostics) = match &tr_def.items { + Some(items) => { + let mut collector = AssocItemCollector::new( + db, + module_id, + tree_id.file_id(), + ItemContainerId::TraitId(tr), + ); + collector.collect(&item_tree, tree_id.tree_id(), items); + collector.finish() + } + None => Default::default(), + }; ( Arc::new(TraitData { name, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 570344596def8..0aa531eff71f6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -666,7 +666,8 @@ pub struct Trait { pub generic_params: Interned, pub is_auto: bool, pub is_unsafe: bool, - pub items: Box<[AssocItem]>, + /// This is [`None`] if this Trait is a trait alias. + pub items: Option>, pub ast_id: FileAstId, } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 79249757d9e9b..b25274bccc9a4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -451,15 +451,7 @@ impl<'a> Ctx<'a> { .collect() }); let ast_id = self.source_ast_id_map.ast_id(trait_def); - let res = Trait { - name, - visibility, - generic_params, - is_auto, - is_unsafe, - items: items.unwrap_or_default(), - ast_id, - }; + let res = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id }; Some(id(self.data().traits.alloc(res))) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index da1643152c2fe..48c40df22ff5f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -375,12 +375,21 @@ impl<'a> Printer<'a> { } w!(self, "trait {}", name); self.print_generic_params(generic_params); - self.print_where_clause_and_opening_brace(generic_params); - self.indented(|this| { - for item in &**items { - this.print_mod_item((*item).into()); + match items { + Some(items) => { + self.print_where_clause_and_opening_brace(generic_params); + self.indented(|this| { + for item in &**items { + this.print_mod_item((*item).into()); + } + }); } - }); + None => { + w!(self, " = "); + // FIXME: Print the aliased traits + self.print_where_clause_and_opening_brace(generic_params); + } + } wln!(self, "}}"); } ModItem::Impl(it) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs index bc162d0fa2069..fc90c6e9f370f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs @@ -94,11 +94,11 @@ macro_rules! m { ($($s:stmt)*) => (stringify!($($s |)*);) } stringify!(; -|; -|92|; -|let x = 92|; +| ; +|92| ; +|let x = 92| ; |loop {} -|; +| ; |); "#]], ); @@ -118,7 +118,7 @@ m!(.. .. ..); macro_rules! m { ($($p:pat)*) => (stringify!($($p |)*);) } -stringify!(.. .. ..|); +stringify!(.. .. .. |); "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs index 029821e5e87f6..118c14ed843fe 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -82,14 +82,14 @@ fn attribute_macro_syntax_completion_2() { #[proc_macros::identity_when_valid] fn foo() { bar.; blub } "#, - expect![[r##" + expect![[r#" #[proc_macros::identity_when_valid] fn foo() { bar.; blub } fn foo() { - bar.; + bar. ; blub -}"##]], +}"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 9ffc218818ca1..b0dd01f9dbea2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -212,6 +212,7 @@ impl Import { #[derive(Debug, Eq, PartialEq)] struct ImportDirective { + /// The module this import directive is in. module_id: LocalModuleId, import: Import, status: PartialResolvedImport, @@ -963,8 +964,10 @@ impl DefCollector<'_> { fn update( &mut self, + // The module for which `resolutions` have been resolve module_id: LocalModuleId, resolutions: &[(Option, PerNs)], + // Visibility this import will have vis: Visibility, import_type: ImportType, ) { @@ -974,6 +977,7 @@ impl DefCollector<'_> { fn update_recursive( &mut self, + // The module for which `resolutions` have been resolve module_id: LocalModuleId, resolutions: &[(Option, PerNs)], // All resolutions are imported with this visibility; the visibilities in diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index 8dfda6df64e7c..20d39ec6cb92e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -73,7 +73,10 @@ impl DefMap { pub(crate) fn resolve_visibility( &self, db: &dyn DefDatabase, + // module to import to original_module: LocalModuleId, + // pub(path) + // ^^^^ this visibility: &RawVisibility, ) -> Option { let mut vis = match visibility { @@ -115,6 +118,7 @@ impl DefMap { &self, db: &dyn DefDatabase, mode: ResolveMode, + // module to import to mut original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, @@ -361,6 +365,9 @@ impl DefMap { ); } }; + + curr_per_ns = curr_per_ns + .filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module)); } ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate)) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index 70dd2eb3ade69..0d90047c28f6f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -58,9 +58,9 @@ extern { "#, expect![[r#" crate - E: t + E: _ S: t v - V: t v + V: _ foo: t crate::foo @@ -307,7 +307,7 @@ pub struct FromLib; Bar: t v crate::foo - Bar: t v + Bar: _ FromLib: t v "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs index b2a6a592cf38b..88a3c76393f08 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs @@ -119,7 +119,7 @@ use foo::*; use foo::bar::*; //- /foo/mod.rs -mod bar; +pub mod bar; fn Foo() {}; pub struct Foo {}; @@ -132,6 +132,7 @@ pub(crate) struct PubCrateStruct; crate Foo: t PubCrateStruct: t v + bar: t foo: t crate::foo @@ -336,3 +337,33 @@ mod d { "#]], ); } + +#[test] +fn glob_name_collision_check_visibility() { + check( + r#" +mod event { + mod serenity { + pub fn Event() {} + } + use serenity::*; + + pub struct Event {} +} + +use event::Event; + "#, + expect![[r#" + crate + Event: t + event: t + + crate::event + Event: t v + serenity: t + + crate::event::serenity + Event: v + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs index ba3bf8b5a5cfa..c575bf7cac255 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs @@ -580,7 +580,7 @@ fn module_resolution_decl_inside_inline_module_in_crate_root() { //- /main.rs mod foo { #[path = "baz.rs"] - mod bar; + pub mod bar; } use self::foo::bar::Baz; diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index 3359c99b3961c..77eb1fd450433 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 893e6fe4b8240..a4abe75626e6d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -4,6 +4,7 @@ use std::mem; use mbe::{SyntheticToken, SyntheticTokenId, TokenMap}; use rustc_hash::FxHashMap; +use smallvec::SmallVec; use syntax::{ ast::{self, AstNode, HasLoopBody}, match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, @@ -292,25 +293,34 @@ pub(crate) fn reverse_fixups( token_map: &TokenMap, undo_info: &SyntaxFixupUndoInfo, ) { - tt.token_trees.retain(|tt| match tt { - tt::TokenTree::Leaf(leaf) => { - token_map.synthetic_token_id(leaf.id()).is_none() - || token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID) - } - tt::TokenTree::Subtree(st) => st.delimiter.map_or(true, |d| { - token_map.synthetic_token_id(d.id).is_none() - || token_map.synthetic_token_id(d.id) != Some(EMPTY_ID) - }), - }); - tt.token_trees.iter_mut().for_each(|tt| match tt { - tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map, undo_info), - tt::TokenTree::Leaf(leaf) => { - if let Some(id) = token_map.synthetic_token_id(leaf.id()) { - let original = &undo_info.original[id.0 as usize]; - *tt = tt::TokenTree::Subtree(original.clone()); + let tts = std::mem::take(&mut tt.token_trees); + tt.token_trees = tts + .into_iter() + .filter(|tt| match tt { + tt::TokenTree::Leaf(leaf) => token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID), + tt::TokenTree::Subtree(st) => { + st.delimiter.map_or(true, |d| token_map.synthetic_token_id(d.id) != Some(EMPTY_ID)) } - } - }); + }) + .flat_map(|tt| match tt { + tt::TokenTree::Subtree(mut tt) => { + reverse_fixups(&mut tt, token_map, undo_info); + SmallVec::from_const([tt.into()]) + } + tt::TokenTree::Leaf(leaf) => { + if let Some(id) = token_map.synthetic_token_id(leaf.id()) { + let original = undo_info.original[id.0 as usize].clone(); + if original.delimiter.is_none() { + original.token_trees.into() + } else { + SmallVec::from_const([original.into()]) + } + } else { + SmallVec::from_const([leaf.into()]) + } + } + }) + .collect(); } #[cfg(test)] @@ -319,6 +329,31 @@ mod tests { use super::reverse_fixups; + // The following three functions are only meant to check partial structural equivalence of + // `TokenTree`s, see the last assertion in `check()`. + fn check_leaf_eq(a: &tt::Leaf, b: &tt::Leaf) -> bool { + match (a, b) { + (tt::Leaf::Literal(a), tt::Leaf::Literal(b)) => a.text == b.text, + (tt::Leaf::Punct(a), tt::Leaf::Punct(b)) => a.char == b.char, + (tt::Leaf::Ident(a), tt::Leaf::Ident(b)) => a.text == b.text, + _ => false, + } + } + + fn check_subtree_eq(a: &tt::Subtree, b: &tt::Subtree) -> bool { + a.delimiter.map(|it| it.kind) == b.delimiter.map(|it| it.kind) + && a.token_trees.len() == b.token_trees.len() + && a.token_trees.iter().zip(&b.token_trees).all(|(a, b)| check_tt_eq(a, b)) + } + + fn check_tt_eq(a: &tt::TokenTree, b: &tt::TokenTree) -> bool { + match (a, b) { + (tt::TokenTree::Leaf(a), tt::TokenTree::Leaf(b)) => check_leaf_eq(a, b), + (tt::TokenTree::Subtree(a), tt::TokenTree::Subtree(b)) => check_subtree_eq(a, b), + _ => false, + } + } + #[track_caller] fn check(ra_fixture: &str, mut expect: Expect) { let parsed = syntax::SourceFile::parse(ra_fixture); @@ -331,17 +366,15 @@ mod tests { fixups.append, ); - let mut actual = tt.to_string(); - actual.push('\n'); + let actual = format!("{}\n", tt); expect.indent(false); expect.assert_eq(&actual); // the fixed-up tree should be syntactically valid let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems); - assert_eq!( - parse.errors(), - &[], + assert!( + parse.errors().is_empty(), "parse has syntax errors. parse tree:\n{:#?}", parse.syntax_node() ); @@ -349,9 +382,12 @@ mod tests { reverse_fixups(&mut tt, &tmap, &fixups.undo_info); // the fixed-up + reversed version should be equivalent to the original input - // (but token IDs don't matter) + // modulo token IDs and `Punct`s' spacing. let (original_as_tt, _) = mbe::syntax_node_to_token_tree(&parsed.syntax_node()); - assert_eq!(tt.to_string(), original_as_tt.to_string()); + assert!( + check_subtree_eq(&tt, &original_as_tt), + "different token tree: {tt:?}, {original_as_tt:?}" + ); } #[test] @@ -468,7 +504,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a .__ra_fixup} +fn foo () {a . __ra_fixup} "#]], ) } @@ -482,7 +518,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a .__ra_fixup ;} +fn foo () {a . __ra_fixup ;} "#]], ) } @@ -497,7 +533,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a .__ra_fixup ; bar () ;} +fn foo () {a . __ra_fixup ; bar () ;} "#]], ) } @@ -525,7 +561,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {let x = a .__ra_fixup ;} +fn foo () {let x = a . __ra_fixup ;} "#]], ) } @@ -541,7 +577,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a .b ; bar () ;} +fn foo () {a . b ; bar () ;} "#]], ) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index a5b499fe8d9d4..7352b003a491c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -814,7 +814,7 @@ impl<'a> InFile<&'a SyntaxNode> { pub fn original_syntax_node(self, db: &dyn db::AstDatabase) -> Option> { // This kind of upmapping can only be achieved in attribute expanded files, - // as we don't have node inputs otherwise and therefor can't find an `N` node in the input + // as we don't have node inputs otherwise and therefore can't find an `N` node in the input if !self.file_id.is_macro() { return Some(self.map(Clone::clone)); } else if !self.file_id.is_attr_macro(db) { @@ -926,7 +926,7 @@ impl InFile { pub fn original_ast_node(self, db: &dyn db::AstDatabase) -> Option> { // This kind of upmapping can only be achieved in attribute expanded files, - // as we don't have node inputs otherwise and therefor can't find an `N` node in the input + // as we don't have node inputs otherwise and therefore can't find an `N` node in the input if !self.file_id.is_macro() { return Some(self); } else if !self.file_id.is_attr_macro(db) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index ed13275bab8fe..a1d6835bfaed3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index b68c764bdca09..39514fc44e6c8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -53,7 +53,7 @@ pub use builder::{ParamKind, TyBuilder}; pub use chalk_ext::*; pub use infer::{ could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, - InferenceResult, + InferenceResult, OverloadedDeref, PointerCast, }; pub use interner::Interner; pub use lower::{ @@ -523,7 +523,7 @@ where } pub fn callable_sig_from_fnonce( - self_ty: &Canonical, + self_ty: &Ty, env: Arc, db: &dyn HirDatabase, ) -> Option { @@ -531,27 +531,28 @@ pub fn callable_sig_from_fnonce( let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; - let mut kinds = self_ty.binders.interned().to_vec(); let b = TyBuilder::trait_ref(db, fn_once_trait); if b.remaining() != 2 { return None; } - let fn_once = b - .push(self_ty.value.clone()) - .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len()) - .build(); - kinds.extend(fn_once.substitution.iter(Interner).skip(1).map(|x| { - let vk = match x.data(Interner) { - chalk_ir::GenericArgData::Ty(_) => { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, - chalk_ir::GenericArgData::Const(c) => { - chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) - } - }; - chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) - })); + let fn_once = b.push(self_ty.clone()).fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build(); + let kinds = fn_once + .substitution + .iter(Interner) + .skip(1) + .map(|x| { + let vk = match x.data(Interner) { + chalk_ir::GenericArgData::Ty(_) => { + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) + } + chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, + chalk_ir::GenericArgData::Const(c) => { + chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) + } + }; + chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) + }) + .collect::>(); // FIXME: chalk refuses to solve `>::Output == ^0.1`, so we first solve // `>` and then replace `^0.0` with the concrete argument tuple. @@ -563,21 +564,16 @@ pub fn callable_sig_from_fnonce( Some(Solution::Unique(vars)) => vars.value.subst, _ => return None, }; - let args = subst.at(Interner, self_ty.binders.interned().len()).ty(Interner)?; + let args = subst.at(Interner, 0).ty(Interner)?; let params = match args.kind(Interner) { chalk_ir::TyKind::Tuple(_, subst) => { subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::>() } _ => return None, }; - if params.iter().any(|ty| ty.is_unknown()) { - return None; - } - let fn_once = TyBuilder::trait_ref(db, fn_once_trait) - .push(self_ty.value.clone()) - .push(args.clone()) - .build(); + let fn_once = + TyBuilder::trait_ref(db, fn_once_trait).push(self_ty.clone()).push(args.clone()).build(); let projection = TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone())) .build(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 50859475e1d94..8bcfa2728f071 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -541,7 +541,7 @@ pub struct ReceiverAdjustments { impl ReceiverAdjustments { pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec) { - let mut ty = ty; + let mut ty = table.resolve_ty_shallow(&ty); let mut adjust = Vec::new(); for _ in 0..self.autoderefs { match autoderef::autoderef_step(table, ty.clone()) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index ac8edb841a580..5d76d185ffc04 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -164,16 +164,16 @@ fn infer_associated_method_with_modules() { check_infer( r#" mod a { - struct A; + pub struct A; impl A { pub fn thing() -> A { A {} }} } mod b { - struct B; + pub struct B; impl B { pub fn thing() -> u32 { 99 }} - mod c { - struct C; + pub mod c { + pub struct C; impl C { pub fn thing() -> C { C {} }} } } @@ -186,22 +186,22 @@ fn infer_associated_method_with_modules() { } "#, expect![[r#" - 55..63 '{ A {} }': A - 57..61 'A {}': A - 125..131 '{ 99 }': u32 - 127..129 '99': u32 - 201..209 '{ C {} }': C - 203..207 'C {}': C - 240..324 '{ ...g(); }': () - 250..251 'x': A - 254..265 'a::A::thing': fn thing() -> A - 254..267 'a::A::thing()': A - 277..278 'y': u32 - 281..292 'b::B::thing': fn thing() -> u32 - 281..294 'b::B::thing()': u32 - 304..305 'z': C - 308..319 'c::C::thing': fn thing() -> C - 308..321 'c::C::thing()': C + 59..67 '{ A {} }': A + 61..65 'A {}': A + 133..139 '{ 99 }': u32 + 135..137 '99': u32 + 217..225 '{ C {} }': C + 219..223 'C {}': C + 256..340 '{ ...g(); }': () + 266..267 'x': A + 270..281 'a::A::thing': fn thing() -> A + 270..283 'a::A::thing()': A + 293..294 'y': u32 + 297..308 'b::B::thing': fn thing() -> u32 + 297..310 'b::B::thing()': u32 + 320..321 'z': C + 324..335 'c::C::thing': fn thing() -> C + 324..337 'c::C::thing()': C "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index a155adcec6c33..4e46397459d5d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -1707,3 +1707,19 @@ impl Trait for [T; N] { "#, ); } + +#[test] +fn unsize_array_with_inference_variable() { + check_types( + r#" +//- minicore: try, slice +use core::ops::ControlFlow; +fn foo() -> ControlFlow<(), [usize; 1]> { loop {} } +fn bar() -> ControlFlow<(), ()> { + let a = foo()?.len(); + //^ usize + ControlFlow::Continue(()) +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 080e2ac1b8e1e..d7431443b83d5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -214,7 +214,7 @@ fn infer_paths() { fn a() -> u32 { 1 } mod b { - fn c() -> u32 { 1 } + pub fn c() -> u32 { 1 } } fn test() { @@ -225,13 +225,13 @@ fn test() { expect![[r#" 14..19 '{ 1 }': u32 16..17 '1': u32 - 47..52 '{ 1 }': u32 - 49..50 '1': u32 - 66..90 '{ ...c(); }': () - 72..73 'a': fn a() -> u32 - 72..75 'a()': u32 - 81..85 'b::c': fn c() -> u32 - 81..87 'b::c()': u32 + 51..56 '{ 1 }': u32 + 53..54 '1': u32 + 70..94 '{ ...c(); }': () + 76..77 'a': fn a() -> u32 + 76..79 'a()': u32 + 85..89 'b::c': fn c() -> u32 + 85..91 'b::c()': u32 "#]], ); } @@ -1856,7 +1856,7 @@ fn not_shadowing_module_by_primitive() { check_types( r#" //- /str.rs -fn foo() -> u32 {0} +pub fn foo() -> u32 {0} //- /main.rs mod str; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 7d42b8b9bc8d8..3d7194b6f4468 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1706,7 +1706,7 @@ fn where_clause_trait_in_scope_for_method_resolution() { check_types( r#" mod foo { - trait Trait { + pub trait Trait { fn foo(&self) -> u32 { 0 } } } @@ -1723,7 +1723,7 @@ fn super_trait_method_resolution() { check_infer( r#" mod foo { - trait SuperTrait { + pub trait SuperTrait { fn foo(&self) -> u32 {} } } @@ -1735,15 +1735,15 @@ fn test(x: T, y: U) { y.foo(); }"#, expect![[r#" - 49..53 'self': &Self - 62..64 '{}': u32 - 181..182 'x': T - 187..188 'y': U - 193..222 '{ ...o(); }': () - 199..200 'x': T - 199..206 'x.foo()': u32 - 212..213 'y': U - 212..219 'y.foo()': u32 + 53..57 'self': &Self + 66..68 '{}': u32 + 185..186 'x': T + 191..192 'y': U + 197..226 '{ ...o(); }': () + 203..204 'x': T + 203..210 'x.foo()': u32 + 216..217 'y': U + 216..223 'y.foo()': u32 "#]], ); } @@ -1754,7 +1754,7 @@ fn super_trait_impl_trait_method_resolution() { r#" //- minicore: sized mod foo { - trait SuperTrait { + pub trait SuperTrait { fn foo(&self) -> u32 {} } } @@ -1764,12 +1764,12 @@ fn test(x: &impl Trait1) { x.foo(); }"#, expect![[r#" - 49..53 'self': &Self - 62..64 '{}': u32 - 115..116 'x': &impl Trait1 - 132..148 '{ ...o(); }': () - 138..139 'x': &impl Trait1 - 138..145 'x.foo()': u32 + 53..57 'self': &Self + 66..68 '{}': u32 + 119..120 'x': &impl Trait1 + 136..152 '{ ...o(); }': () + 142..143 'x': &impl Trait1 + 142..149 'x.foo()': u32 "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index e1418de3cdc24..f780e3f53c855 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index cbd9bf32a5486..cbbcaebb42855 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -117,7 +117,7 @@ pub use { name::{known, Name}, ExpandResult, HirFileId, InFile, MacroFile, Origin, }, - hir_ty::display::HirDisplay, + hir_ty::{display::HirDisplay, PointerCast, Safety}, }; // These are negative re-exports: pub using these names is forbidden, they @@ -2997,8 +2997,7 @@ impl Type { TyKind::Function(_) => Callee::FnPtr, TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?), _ => { - let ty = hir_ty::replace_errors_with_variables(&self.ty); - let sig = hir_ty::callable_sig_from_fnonce(&ty, self.env.clone(), db)?; + let sig = hir_ty::callable_sig_from_fnonce(&self.ty, self.env.clone(), db)?; return Some(Callable { ty: self.clone(), sig, @@ -3651,6 +3650,28 @@ impl From for ScopeDef { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Adjust { + /// Go from ! to any type. + NeverToAny, + /// Dereference once, producing a place. + Deref(Option), + /// Take the address and produce either a `&` or `*` pointer. + Borrow(AutoBorrow), + Pointer(PointerCast), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum AutoBorrow { + /// Converts from T to &T. + Ref(Mutability), + /// Converts from T to *T. + RawPtr(Mutability), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct OverloadedDeref(pub Mutability); + pub trait HasVisibility { fn visibility(&self, db: &dyn HirDatabase) -> Visibility; fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 119ec3210e175..2e1f88ba09043 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -29,9 +29,10 @@ use crate::{ db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{resolve_hir_path, SourceAnalyzer}, - Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function, - HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, - Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef, + Access, Adjust, AutoBorrow, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, + DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, + Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, ToolModule, Trait, Type, + TypeAlias, TypeParam, VariantDef, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -333,9 +334,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_trait(trait_) } - // FIXME: Figure out a nice interface to inspect adjustments - pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option { - self.imp.is_implicit_reborrow(expr) + pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option> { + self.imp.expr_adjustments(expr) } pub fn type_of_expr(&self, expr: &ast::Expr) -> Option { @@ -1067,8 +1067,29 @@ impl<'db> SemanticsImpl<'db> { } } - fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option { - self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr) + fn expr_adjustments(&self, expr: &ast::Expr) -> Option> { + let mutability = |m| match m { + hir_ty::Mutability::Not => Mutability::Shared, + hir_ty::Mutability::Mut => Mutability::Mut, + }; + self.analyze(expr.syntax())?.expr_adjustments(self.db, expr).map(|it| { + it.iter() + .map(|adjust| match adjust.kind { + hir_ty::Adjust::NeverToAny => Adjust::NeverToAny, + hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => { + Adjust::Deref(Some(OverloadedDeref(mutability(m)))) + } + hir_ty::Adjust::Deref(None) => Adjust::Deref(None), + hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => { + Adjust::Borrow(AutoBorrow::RawPtr(mutability(m))) + } + hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::Ref(m)) => { + Adjust::Borrow(AutoBorrow::Ref(mutability(m))) + } + hir_ty::Adjust::Pointer(pc) => Adjust::Pointer(pc), + }) + .collect() + }) } fn type_of_expr(&self, expr: &ast::Expr) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index f86c571005367..91ea1c24d14f8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -38,8 +38,7 @@ use hir_ty::{ UnsafeExpr, }, method_resolution::{self, lang_names_for_bin_op}, - Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, - TyLoweringContext, + Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext, }; use itertools::Itertools; use smallvec::SmallVec; @@ -156,21 +155,14 @@ impl SourceAnalyzer { Some(res) } - pub(crate) fn is_implicit_reborrow( + pub(crate) fn expr_adjustments( &self, db: &dyn HirDatabase, expr: &ast::Expr, - ) -> Option { + ) -> Option<&[Adjustment]> { let expr_id = self.expr_id(db, expr)?; let infer = self.infer.as_ref()?; - let adjustments = infer.expr_adjustments.get(&expr_id)?; - adjustments.windows(2).find_map(|slice| match slice { - &[Adjustment {kind: Adjust::Deref(None), ..}, Adjustment {kind: Adjust::Borrow(AutoBorrow::Ref(m)), ..}] => Some(match m { - hir_ty::Mutability::Mut => Mutability::Mut, - hir_ty::Mutability::Not => Mutability::Shared, - }), - _ => None, - }) + infer.expr_adjustments.get(&expr_id).map(|v| &**v) } pub(crate) fn type_of_expr( diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml index 57a41f3d9a937..e781c0a016d5a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 62cf5ab4f37a1..2b3793659cf7d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -196,6 +196,7 @@ trait Foo { type Output; const CONST: usize = 42; + const CONST_2: i32; fn foo(&self); fn bar(&self); @@ -213,6 +214,7 @@ trait Foo { type Output; const CONST: usize = 42; + const CONST_2: i32; fn foo(&self); fn bar(&self); @@ -226,7 +228,7 @@ impl Foo for S { $0type Output; - const CONST: usize = 42; + const CONST_2: i32; fn foo(&self) { todo!() @@ -379,14 +381,14 @@ impl Foo for S { r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { $0 }"#, r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { @@ -439,14 +441,14 @@ impl bar::Foo for S { r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { $0 }"#, r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { @@ -464,14 +466,14 @@ impl foo::Foo for S { r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { $0 }"#, r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { @@ -489,7 +491,7 @@ impl foo::Foo for S { add_missing_impl_members, r#" mod foo { - trait Foo { fn foo(&self, bar: T); } + pub trait Foo { fn foo(&self, bar: T); } pub struct Param; } struct Param; @@ -497,7 +499,7 @@ struct S; impl foo::Foo for S { $0 }"#, r#" mod foo { - trait Foo { fn foo(&self, bar: T); } + pub trait Foo { fn foo(&self, bar: T); } pub struct Param; } struct Param; @@ -518,7 +520,7 @@ impl foo::Foo for S { mod foo { pub struct Bar; impl Bar { type Assoc = u32; } - trait Foo { fn foo(&self, bar: Bar::Assoc); } + pub trait Foo { fn foo(&self, bar: Bar::Assoc); } } struct S; impl foo::Foo for S { $0 }"#, @@ -526,7 +528,7 @@ impl foo::Foo for S { $0 }"#, mod foo { pub struct Bar; impl Bar { type Assoc = u32; } - trait Foo { fn foo(&self, bar: Bar::Assoc); } + pub trait Foo { fn foo(&self, bar: Bar::Assoc); } } struct S; impl foo::Foo for S { @@ -545,7 +547,7 @@ impl foo::Foo for S { mod foo { pub struct Bar; pub struct Baz; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { $0 }"#, @@ -553,7 +555,7 @@ impl foo::Foo for S { $0 }"#, mod foo { pub struct Bar; pub struct Baz; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { @@ -571,14 +573,14 @@ impl foo::Foo for S { r#" mod foo { pub trait Fn { type Output; } - trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } + pub trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } } struct S; impl foo::Foo for S { $0 }"#, r#" mod foo { pub trait Fn { type Output; } - trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } + pub trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } } struct S; impl foo::Foo for S { @@ -658,6 +660,7 @@ trait Foo { type Output; const CONST: usize = 42; + const CONST_2: i32; fn valid(some: u32) -> bool { false } fn foo(some: u32) -> bool; @@ -669,13 +672,16 @@ trait Foo { type Output; const CONST: usize = 42; + const CONST_2: i32; fn valid(some: u32) -> bool { false } fn foo(some: u32) -> bool; } struct S; impl Foo for S { - $0fn valid(some: u32) -> bool { false } + $0const CONST: usize = 42; + + fn valid(some: u32) -> bool { false } }"#, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 0605883584922..c1e2f19ab18b2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -109,8 +109,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let params = body.extracted_function_params(ctx, &container_info, locals_used.iter().copied()); - let extracted_from_trait_impl = body.extracted_from_trait_impl(); - let name = make_function_name(&semantics_scope); let fun = Function { @@ -129,8 +127,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op builder.replace(target_range, make_call(ctx, &fun, old_indent)); + let has_impl_wrapper = + insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after); + let fn_def = match fun.self_param_adt(ctx) { - Some(adt) if extracted_from_trait_impl => { + Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => { let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1); generate_impl_text(&adt, &fn_def).replace("{\n\n", "{") } @@ -272,7 +273,7 @@ enum FunType { } /// Where to put extracted function definition -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq, Clone, Copy)] enum Anchor { /// Extract free function and put right after current top-level function Freestanding, @@ -1245,6 +1246,14 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option break, + SyntaxKind::IMPL => { + if body.extracted_from_trait_impl() && matches!(anchor, Anchor::Method) { + let impl_node = find_non_trait_impl(&next_ancestor); + if let target_node @ Some(_) = impl_node.as_ref().and_then(last_impl_member) { + return target_node; + } + } + } SyntaxKind::ITEM_LIST if !matches!(anchor, Anchor::Freestanding) => continue, SyntaxKind::ITEM_LIST => { if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) { @@ -1265,6 +1274,29 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option Option { + let as_impl = ast::Impl::cast(trait_impl.clone())?; + let impl_type = Some(impl_type_name(&as_impl)?); + + let sibblings = trait_impl.parent()?.children(); + sibblings + .filter_map(ast::Impl::cast) + .find(|s| impl_type_name(s) == impl_type && !is_trait_impl(s)) +} + +fn last_impl_member(impl_node: &ast::Impl) -> Option { + let last_child = impl_node.assoc_item_list()?.assoc_items().last()?; + Some(last_child.syntax().clone()) +} + +fn is_trait_impl(node: &ast::Impl) -> bool { + node.trait_().is_some() +} + +fn impl_type_name(impl_node: &ast::Impl) -> Option { + Some(impl_node.self_ty()?.to_string()) +} + fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> String { let ret_ty = fun.return_type(ctx); @@ -5051,6 +5083,236 @@ impl Struct { ); } + #[test] + fn extract_method_from_trait_with_existing_non_empty_impl_block() { + check_assist( + extract_function, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl Struct { + fn foo() {} +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + $0self.0 + 2$0 + } +} +"#, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl Struct { + fn foo() {} + + fn $0fun_name(&self) -> i32 { + self.0 + 2 + } +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + self.fun_name() + } +} +"#, + ) + } + + #[test] + fn extract_function_from_trait_with_existing_non_empty_impl_block() { + check_assist( + extract_function, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl Struct { + fn foo() {} +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + let three_squared = $03 * 3$0; + self.0 + three_squared + } +} +"#, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl Struct { + fn foo() {} +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + let three_squared = fun_name(); + self.0 + three_squared + } +} + +fn $0fun_name() -> i32 { + 3 * 3 +} +"#, + ) + } + + #[test] + fn extract_method_from_trait_with_multiple_existing_impl_blocks() { + check_assist( + extract_function, + r#" +struct Struct(i32); +struct StructBefore(i32); +struct StructAfter(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl StructBefore { + fn foo(){} +} + +impl Struct { + fn foo(){} +} + +impl StructAfter { + fn foo(){} +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + $0self.0 + 2$0 + } +} +"#, + r#" +struct Struct(i32); +struct StructBefore(i32); +struct StructAfter(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl StructBefore { + fn foo(){} +} + +impl Struct { + fn foo(){} + + fn $0fun_name(&self) -> i32 { + self.0 + 2 + } +} + +impl StructAfter { + fn foo(){} +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + self.fun_name() + } +} +"#, + ) + } + + #[test] + fn extract_method_from_trait_with_multiple_existing_trait_impl_blocks() { + check_assist( + extract_function, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} +trait TraitBefore { + fn before(&self) -> i32; +} +trait TraitAfter { + fn after(&self) -> i32; +} + +impl TraitBefore for Struct { + fn before(&self) -> i32 { + 42 + } +} + +impl Struct { + fn foo(){} +} + +impl TraitAfter for Struct { + fn after(&self) -> i32 { + 42 + } +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + $0self.0 + 2$0 + } +} +"#, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} +trait TraitBefore { + fn before(&self) -> i32; +} +trait TraitAfter { + fn after(&self) -> i32; +} + +impl TraitBefore for Struct { + fn before(&self) -> i32 { + 42 + } +} + +impl Struct { + fn foo(){} + + fn $0fun_name(&self) -> i32 { + self.0 + 2 + } +} + +impl TraitAfter for Struct { + fn after(&self) -> i32 { + 42 + } +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + self.fun_name() + } +} +"#, + ) + } + #[test] fn closure_arguments() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index 8764543028706..d9e00435ecf5d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -1,4 +1,4 @@ -use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; +use hir::{db::HirDatabase, HasSource, HasVisibility, ModuleDef, PathResolution, ScopeDef}; use ide_db::base_db::FileId; use syntax::{ ast::{self, HasVisibility as _}, @@ -18,7 +18,7 @@ use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; // fn frobnicate() {} // } // fn main() { -// m::frobnicate$0() {} +// m::frobnicate$0(); // } // ``` // -> @@ -27,7 +27,7 @@ use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; // $0pub(crate) fn frobnicate() {} // } // fn main() { -// m::frobnicate() {} +// m::frobnicate(); // } // ``` pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { @@ -37,11 +37,15 @@ pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let path: ast::Path = ctx.find_node_at_offset()?; - let path_res = ctx.sema.resolve_path(&path)?; - let def = match path_res { - PathResolution::Def(def) => def, - _ => return None, - }; + let qualifier = path.qualifier()?; + let name_ref = path.segment()?.name_ref()?; + let qualifier_res = ctx.sema.resolve_path(&qualifier)?; + let PathResolution::Def(ModuleDef::Module(module)) = qualifier_res else { return None; }; + let (_, def) = module + .scope(ctx.db(), None) + .into_iter() + .find(|(name, _)| name.to_smol_str() == name_ref.text().as_str())?; + let ScopeDef::ModuleDef(def) = def else { return None; }; let current_module = ctx.sema.scope(path.syntax())?.module(); let target_module = def.module(ctx.db())?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs index 35cd42908af2c..0bcb5728311b7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -261,12 +261,12 @@ fn main() { } //- /foo.rs -enum Foo { +pub enum Foo { Bar, } ", r" -enum Foo { +pub enum Foo { Bar, Baz, } @@ -310,7 +310,7 @@ fn main() { generate_enum_variant, r" mod m { - enum Foo { + pub enum Foo { Bar, } } @@ -320,7 +320,7 @@ fn main() { ", r" mod m { - enum Foo { + pub enum Foo { Bar, Baz, } @@ -516,10 +516,10 @@ mod foo; use foo::Foo::Bar$0; //- /foo.rs -enum Foo {} +pub enum Foo {} ", r" -enum Foo { +pub enum Foo { Bar, } ", diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index c229127e48ffc..57f198748cb76 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -1324,7 +1324,7 @@ fn foo() { generate_function, r" mod bar { - mod baz {} + pub mod baz {} } fn foo() { @@ -1333,7 +1333,7 @@ fn foo() { ", r" mod bar { - mod baz { + pub mod baz { pub(crate) fn my_fn() { ${0:todo!()} } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs index aa710d2ce6513..11db6ae7f7b81 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs @@ -92,7 +92,7 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>) NodeOrToken::Node(n) => { format_to!(current_arg, "{n}"); }, - NodeOrToken::Token(t) if t.kind() == COMMA=> { + NodeOrToken::Token(t) if t.kind() == COMMA => { existing_args.push(current_arg.trim().into()); current_arg.clear(); }, @@ -238,14 +238,14 @@ fn main() { &add_macro_decl( r#" fn main() { - print!("{} {x + 1:b} {Struct(1, 2)}$0", 1); + print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1); } "#, ), &add_macro_decl( r#" fn main() { - print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2)); + print!("{:b} {:b} {}"$0, 1, x + 1, Struct(1, 2)); } "#, ), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index 3d9cbff177ba9..99ae60e07bcfa 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs @@ -1,7 +1,7 @@ use itertools::Itertools; use syntax::{ ast::{self, AstNode, AstToken}, - match_ast, NodeOrToken, SyntaxElement, TextSize, T, + match_ast, NodeOrToken, SyntaxElement, TextRange, TextSize, T, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -22,7 +22,36 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // } // ``` pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let macro_call = ctx.find_node_at_offset::()?; + let macro_calls = if ctx.has_empty_selection() { + vec![ctx.find_node_at_offset::()?] + } else { + ctx.covering_element() + .as_node()? + .descendants() + .filter(|node| ctx.selection_trimmed().contains_range(node.text_range())) + .filter_map(ast::MacroCall::cast) + .collect() + }; + + let replacements = + macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::>(); + if replacements.is_empty() { + return None; + } + + acc.add( + AssistId("remove_dbg", AssistKind::Refactor), + "Remove dbg!()", + ctx.selection_trimmed(), + |builder| { + for (range, text) in replacements { + builder.replace(range, text); + } + }, + ) +} + +fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, String)> { let tt = macro_call.token_tree()?; let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?); if macro_call.path()?.segment()?.name_ref()?.text() != "dbg" @@ -41,7 +70,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?; let parent = macro_expr.syntax().parent()?; - let (range, text) = match &*input_expressions { + Some(match &*input_expressions { // dbg!() [] => { match_ast! { @@ -107,10 +136,6 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( } // dbg!(expr0, expr1, ...) exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))), - }; - - acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", range, |builder| { - builder.replace(range, text); }) } @@ -238,4 +263,28 @@ fn foo() { check(r#"$0dbg!(0, 1)"#, r#"(0, 1)"#); check(r#"$0dbg!(0, (1, 2))"#, r#"(0, (1, 2))"#); } + + #[test] + fn test_range() { + check( + r#" +fn f() { + dbg!(0) + $0dbg!(1); + dbg!(())$0 +} +"#, + r#" +fn f() { + dbg!(0) + 1; + () +} +"#, + ); + } + + #[test] + fn test_range_partial() { + check_assist_not_applicable(remove_dbg, r#"$0dbg$0!(0)"#); + check_assist_not_applicable(remove_dbg, r#"$0dbg!(0$0)"#); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index f9ba289ee175f..6fa15b28e4efc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1019,8 +1019,6 @@ struct Foo { impl foo::Bar for Foo { $0type Qux; - const Baz: usize = 42; - const Fez: usize; fn foo() { 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 029d169899bb4..c09317572acf2 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 @@ -741,7 +741,7 @@ mod m { fn frobnicate() {} } fn main() { - m::frobnicate$0() {} + m::frobnicate$0(); } "#####, r#####" @@ -749,7 +749,7 @@ mod m { $0pub(crate) fn frobnicate() {} } fn main() { - m::frobnicate() {} + m::frobnicate(); } "#####, ) 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 307e67927056b..68c31b4f8e922 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -119,6 +119,10 @@ pub fn filter_assoc_items( (default_methods, def.body()), (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None) ), + ast::AssocItem::Const(def) => matches!( + (default_methods, def.body()), + (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None) + ), _ => default_methods == DefaultMethods::No, }) .collect::>() diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml index 75835bce95da1..11310e2f1291e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index e82cbfdcb8402..7384a3f2d80b4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -157,7 +157,7 @@ fn complete_trait_impl( add_function_impl(acc, ctx, replacement_range, func, hir_impl) } (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => { - add_type_alias_impl(acc, ctx, replacement_range, type_alias) + add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl) } (hir::AssocItem::Const(const_), All | Const) => { add_const_impl(acc, ctx, replacement_range, const_, hir_impl) @@ -236,9 +236,7 @@ fn get_transformed_assoc_item( ); transform.apply(assoc_item.syntax()); - if let ast::AssocItem::Fn(func) = &assoc_item { - func.remove_attrs_and_docs(); - } + assoc_item.remove_attrs_and_docs(); Some(assoc_item) } @@ -247,24 +245,50 @@ fn add_type_alias_impl( ctx: &CompletionContext<'_>, replacement_range: TextRange, type_alias: hir::TypeAlias, + impl_def: hir::Impl, ) { - let alias_name = type_alias.name(ctx.db); - let (alias_name, escaped_name) = - (alias_name.unescaped().to_smol_str(), alias_name.to_smol_str()); + let alias_name = type_alias.name(ctx.db).unescaped().to_smol_str(); let label = format!("type {} =", alias_name); - let replacement = format!("type {} = ", escaped_name); let mut item = CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label); item.lookup_by(format!("type {}", alias_name)) .set_documentation(type_alias.docs(ctx.db)) .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); - match ctx.config.snippet_cap { - Some(cap) => item - .snippet_edit(cap, TextEdit::replace(replacement_range, format!("{}$0;", replacement))), - None => item.text_edit(TextEdit::replace(replacement_range, replacement)), - }; - item.add_to(acc); + + if let Some(source) = ctx.sema.source(type_alias) { + let assoc_item = ast::AssocItem::TypeAlias(source.value); + if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { + let transformed_ty = match transformed_item { + ast::AssocItem::TypeAlias(ty) => ty, + _ => unreachable!(), + }; + + let start = transformed_ty.syntax().text_range().start(); + let Some(end) = transformed_ty + .eq_token() + .map(|tok| tok.text_range().start()) + .or(transformed_ty.semicolon_token().map(|tok| tok.text_range().start())) else { return }; + + let len = end - start; + let mut decl = transformed_ty.syntax().text().slice(..len).to_string(); + if !decl.ends_with(' ') { + decl.push(' '); + } + decl.push_str("= "); + + match ctx.config.snippet_cap { + Some(cap) => { + let snippet = format!("{}$0;", decl); + item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet)); + } + None => { + item.text_edit(TextEdit::replace(replacement_range, decl)); + } + }; + item.add_to(acc); + } + } } fn add_const_impl( @@ -309,7 +333,6 @@ fn add_const_impl( } fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> String { - const_.remove_attrs_and_docs(); let const_ = if needs_whitespace { insert_whitespace_into_node::insert_ws_into(const_.syntax().clone()) } else { @@ -333,8 +356,6 @@ fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> Strin } fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String { - node.remove_attrs_and_docs(); - let node = if needs_whitespace { insert_whitespace_into_node::insert_ws_into(node.syntax().clone()) } else { @@ -350,9 +371,7 @@ fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String { .map_or(end, |f| f.text_range().start()); let len = end - start; - let range = TextRange::new(0.into(), len); - - let syntax = node.text().slice(range).to_string(); + let syntax = node.text().slice(..len).to_string(); syntax.trim_end().to_owned() } @@ -1160,6 +1179,106 @@ impl Foo for Test { $0 } } +"#, + ); + } + + #[test] + fn includes_gat_generics() { + check_edit( + "type Ty", + r#" +trait Tr<'b> { + type Ty<'a: 'b, T: Copy, const C: usize>; +} + +impl<'b> Tr<'b> for () { + $0 +} +"#, + r#" +trait Tr<'b> { + type Ty<'a: 'b, T: Copy, const C: usize>; +} + +impl<'b> Tr<'b> for () { + type Ty<'a: 'b, T: Copy, const C: usize> = $0; +} +"#, + ); + } + + #[test] + fn strips_comments() { + check_edit( + "fn func", + r#" +trait Tr { + /// docs + #[attr] + fn func(); +} +impl Tr for () { + $0 +} +"#, + r#" +trait Tr { + /// docs + #[attr] + fn func(); +} +impl Tr for () { + fn func() { + $0 +} +} +"#, + ); + check_edit( + "const C", + r#" +trait Tr { + /// docs + #[attr] + const C: usize; +} +impl Tr for () { + $0 +} +"#, + r#" +trait Tr { + /// docs + #[attr] + const C: usize; +} +impl Tr for () { + const C: usize = $0; +} +"#, + ); + check_edit( + "type Item", + r#" +trait Tr { + /// docs + #[attr] + type Item; +} +impl Tr for () { + $0 +} +"#, + r#" +trait Tr { + /// docs + #[attr] + type Item; +} +impl Tr for () { + type Item = $0; +} "#, ); } 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 04111ec7efaa6..c142a7305f9e9 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 @@ -681,9 +681,13 @@ fn classify_name_ref( ast::Item::ExternBlock(it) => it.extern_item_list().is_none(), ast::Item::Fn(it) => it.body().is_none(), ast::Item::Impl(it) => it.assoc_item_list().is_none(), - ast::Item::Module(it) => it.item_list().is_none(), + ast::Item::Module(it) => { + it.item_list().is_none() && it.semicolon_token().is_none() + } ast::Item::Static(it) => it.body().is_none(), - ast::Item::Struct(it) => it.field_list().is_none(), + ast::Item::Struct(it) => { + it.field_list().is_none() && it.semicolon_token().is_none() + } ast::Item::Trait(it) => it.assoc_item_list().is_none(), ast::Item::TypeAlias(it) => it.ty().is_none(), ast::Item::Union(it) => it.record_field_list().is_none(), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs index 5076c6e86caee..8ed6cb3cf867e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs @@ -245,3 +245,35 @@ impl Test for () { "#]], ); } + +#[test] +fn after_unit_struct() { + check( + r#"struct S; f$0"#, + expect![[r#" + ma makro!(…) macro_rules! makro + md module + kw const + kw crate:: + kw enum + kw extern + kw fn + kw impl + kw mod + kw pub + kw pub(crate) + kw pub(super) + kw self:: + kw static + kw struct + kw trait + kw type + kw union + kw unsafe + kw use + sn macro_rules + sn tfn (Test function) + sn tmod (Test module) + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index cf0bcd5c96b2a..f48cce58c6e73 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs index 75d49ff2fd77f..1b8f56187a02b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs @@ -58,8 +58,11 @@ impl LineIndex { let mut utf16_lines = NoHashHashMap::default(); let mut utf16_chars = Vec::new(); - let mut newlines = vec![0.into()]; - let mut curr_row @ mut curr_col = 0.into(); + let mut newlines = Vec::with_capacity(16); + newlines.push(TextSize::from(0)); + + let mut curr_row = 0.into(); + let mut curr_col = 0.into(); let mut line = 0; for c in text.chars() { let c_len = TextSize::of(c); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs index ac6c6e8feeea0..313346ee13153 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs @@ -104,6 +104,11 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec), ()> { extracted_expressions.push(Arg::Placeholder); state = State::NotArg; } + (State::MaybeArg, ':') => { + output.push(chr); + extracted_expressions.push(Arg::Placeholder); + state = State::FormatOpts; + } (State::MaybeArg, _) => { if matches!(chr, '\\' | '$') { current_expr.push('\\'); @@ -118,44 +123,41 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec), ()> { state = State::Expr; } } - (State::Ident | State::Expr, '}') => { - if inexpr_open_count == 0 { - output.push(chr); - - if matches!(state, State::Expr) { - extracted_expressions.push(Arg::Expr(current_expr.trim().into())); - } else { - extracted_expressions.push(Arg::Ident(current_expr.trim().into())); - } - - current_expr = String::new(); - state = State::NotArg; - } else { - // We're closing one brace met before inside of the expression. - current_expr.push(chr); - inexpr_open_count -= 1; - } - } (State::Ident | State::Expr, ':') if matches!(chars.peek(), Some(':')) => { // path separator state = State::Expr; current_expr.push_str("::"); chars.next(); } - (State::Ident | State::Expr, ':') => { + (State::Ident | State::Expr, ':' | '}') => { if inexpr_open_count == 0 { - // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}" - output.push(chr); + let trimmed = current_expr.trim(); - if matches!(state, State::Expr) { - extracted_expressions.push(Arg::Expr(current_expr.trim().into())); + // if the expression consists of a single number, like "0" or "12", it can refer to + // format args in the order they are specified. + // see: https://doc.rust-lang.org/std/fmt/#positional-parameters + if trimmed.chars().fold(true, |only_num, c| c.is_ascii_digit() && only_num) { + output.push_str(trimmed); + } else if matches!(state, State::Expr) { + extracted_expressions.push(Arg::Expr(trimmed.into())); } else { - extracted_expressions.push(Arg::Ident(current_expr.trim().into())); + extracted_expressions.push(Arg::Ident(trimmed.into())); } - current_expr = String::new(); - state = State::FormatOpts; - } else { + output.push(chr); + current_expr.clear(); + state = if chr == ':' { + State::FormatOpts + } else if chr == '}' { + State::NotArg + } else { + unreachable!() + }; + } else if chr == '}' { + // We're closing one brace met before inside of the expression. + current_expr.push(chr); + inexpr_open_count -= 1; + } else if chr == ':' { // We're inside of braced expression, assume that it's a struct field name/value delimiter. current_expr.push(chr); } @@ -219,6 +221,10 @@ mod tests { ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]), ("{expr:?}", expect![["{:?}; expr"]]), ("{expr:1$}", expect![[r"{:1\$}; expr"]]), + ("{:1$}", expect![[r"{:1\$}; $1"]]), + ("{:>padding$}", expect![[r"{:>padding\$}; $1"]]), + ("{}, {}, {0}", expect![[r"{}, {}, {0}; $1, $2"]]), + ("{}, {}, {0:b}", expect![[r"{}, {}, {0:b}; $1, $2"]]), ("{$0}", expect![[r"{}; \$0"]]), ("{malformed", expect![["-"]]), ("malformed}", expect![["-"]]), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index e1d146f4ee561..7e9a1125d751c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index 43ff4ed5a6c86..870c78d1f1eb7 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -5,10 +5,7 @@ use crate::{Diagnostic, DiagnosticsContext}; // This diagnostic is shown for macro expansion errors. pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic { // Use more accurate position if available. - let display_range = d - .precise_location - .unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone()).range); - + let display_range = ctx.resolve_precise_location(&d.node, d.precise_location); Diagnostic::new("macro-error", d.message.clone(), display_range).experimental() } 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 a80299106bd36..d8f2a9de9818f 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 @@ -268,12 +268,12 @@ fn main() { foo::Foo { bar: 3, $0baz: false}; } //- /foo.rs -struct Foo { +pub struct Foo { bar: i32 } "#, r#" -struct Foo { +pub struct Foo { bar: i32, pub(crate) baz: bool } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 4b43124757f06..87531f4acfb75 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs @@ -9,10 +9,7 @@ pub(crate) fn unresolved_macro_call( d: &hir::UnresolvedMacroCall, ) -> Diagnostic { // Use more accurate position if available. - let display_range = d - .precise_location - .unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.macro_call.clone()).range); - + let display_range = ctx.resolve_precise_location(&d.macro_call, d.precise_location); let bang = if d.is_bang { "!" } else { "" }; Diagnostic::new( "unresolved-macro-call", diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs index 760f51f90498c..23818d883f731 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs @@ -1,5 +1,4 @@ use hir::db::DefDatabase; -use syntax::NodeOrToken; use crate::{Diagnostic, DiagnosticsContext, Severity}; @@ -19,16 +18,7 @@ pub(crate) fn unresolved_proc_macro( proc_attr_macros_enabled: bool, ) -> Diagnostic { // Use more accurate position if available. - let display_range = (|| { - let precise_location = d.precise_location?; - let root = ctx.sema.parse_or_expand(d.node.file_id)?; - match root.covering_element(precise_location) { - NodeOrToken::Node(it) => Some(ctx.sema.original_range(&it)), - NodeOrToken::Token(it) => d.node.with_value(it).original_file_range_opt(ctx.sema.db), - } - })() - .unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone())) - .range; + let display_range = ctx.resolve_precise_location(&d.node, d.precise_location); let config_enabled = match d.kind { hir::MacroKind::Attr => proc_macros_enabled && proc_attr_macros_enabled, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs index 8b9330e040137..289ed0458c67d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -71,9 +71,9 @@ use a; use a::{c, d::e}; mod a { - mod c {} - mod d { - mod e {} + pub mod c {} + pub mod d { + pub mod e {} } } "#, @@ -87,9 +87,9 @@ use a::{ }; mod a { - mod c {} - mod d { - mod e {} + pub mod c {} + pub mod d { + pub mod e {} } } "#, @@ -116,11 +116,11 @@ use b; ); check_fix( r#" -mod a { mod c {} } +mod a { pub mod c {} } use a::{c$0}; "#, r#" -mod a { mod c {} } +mod a { pub mod c {} } use a::c; "#, ); @@ -136,11 +136,11 @@ use a; ); check_fix( r#" -mod a { mod c {} mod d { mod e {} } } +mod a { pub mod c {} pub mod d { pub mod e {} } } use a::{c, d::{e$0}}; "#, r#" -mod a { mod c {} mod d { mod e {} } } +mod a { pub mod c {} pub mod d { pub mod e {} } } use a::{c, d::e}; "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index ae299f0584148..d81e36a1f8632 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -182,6 +182,28 @@ struct DiagnosticsContext<'a> { resolve: &'a AssistResolveStrategy, } +impl<'a> DiagnosticsContext<'a> { + fn resolve_precise_location( + &self, + node: &InFile, + precise_location: Option, + ) -> TextRange { + let sema = &self.sema; + (|| { + let precise_location = precise_location?; + let root = sema.parse_or_expand(node.file_id)?; + match root.covering_element(precise_location) { + syntax::NodeOrToken::Node(it) => Some(sema.original_range(&it)), + syntax::NodeOrToken::Token(it) => { + node.with_value(it).original_file_range_opt(sema.db) + } + } + })() + .unwrap_or_else(|| sema.diagnostics_display_range(node.clone())) + .range + } +} + pub fn diagnostics( db: &RootDatabase, config: &DiagnosticsConfig, diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml index 4baf786c45552..7be62a8d9ffe9 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml @@ -5,7 +5,7 @@ description = "Structural search and replace of Rust code" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index 712459a7ee9c6..73f202630f15b 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f97c67b144ac1..43f7a529bc297 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -289,10 +289,10 @@ mod b; enum E { X(Foo$0) } //- /a.rs -struct Foo; - //^^^ +pub struct Foo; + //^^^ //- /b.rs -struct Foo; +pub struct Foo; "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 3687b597fc642..838fb18c3d590 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -119,7 +119,14 @@ pub(crate) fn hover( }); } - let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))); + let in_attr = original_token + .parent_ancestors() + .filter_map(ast::Item::cast) + .any(|item| sema.is_attr_macro_call(&item)) + && !matches!( + original_token.parent().and_then(ast::TokenTree::cast), + Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind())) + ); // prefer descending the same token kind in attribute expansions, in normal macros text // equivalency is more important let descended = if in_attr { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 34d8bf67a3016..37384c4e7e075 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -1,7 +1,10 @@ use std::fmt; use either::Either; -use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo}; +use hir::{ + known, Adjust, AutoBorrow, Callable, HasVisibility, HirDisplay, Mutability, OverloadedDeref, + PointerCast, Safety, Semantics, TypeInfo, +}; use ide_db::{ base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap, RootDatabase, @@ -22,7 +25,7 @@ pub struct InlayHintsConfig { pub type_hints: bool, pub parameter_hints: bool, pub chaining_hints: bool, - pub reborrow_hints: ReborrowHints, + pub adjustment_hints: AdjustmentHints, pub closure_return_type_hints: ClosureReturnTypeHints, pub binding_mode_hints: bool, pub lifetime_elision_hints: LifetimeElisionHints, @@ -48,9 +51,9 @@ pub enum LifetimeElisionHints { } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum ReborrowHints { +pub enum AdjustmentHints { Always, - MutableOnly, + ReborrowOnly, Never, } @@ -61,7 +64,8 @@ pub enum InlayKind { ClosingBraceHint, ClosureReturnTypeHint, GenericParamListHint, - ImplicitReborrowHint, + AdjustmentHint, + AdjustmentHintClosingParenthesis, LifetimeHint, ParameterHint, TypeHint, @@ -115,6 +119,12 @@ impl From for InlayHintLabel { } } +impl From<&str> for InlayHintLabel { + fn from(s: &str) -> Self { + Self { parts: vec![InlayHintLabelPart { text: s.into(), linked_location: None }] } + } +} + impl fmt::Display for InlayHintLabel { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.parts.iter().map(|part| &part.text).format("")) @@ -180,7 +190,7 @@ impl fmt::Debug for InlayHintLabelPart { pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, - range_limit: Option, + range_limit: Option, config: &InlayHintsConfig, ) -> Vec { let _p = profile::span("inlay_hints"); @@ -195,7 +205,7 @@ pub(crate) fn inlay_hints( let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); match range_limit { - Some(FileRange { range, .. }) => match file.covering_element(range) { + Some(range) => match file.covering_element(range) { NodeOrToken::Token(_) => return acc, NodeOrToken::Node(n) => n .descendants() @@ -221,6 +231,7 @@ fn hints( match node { ast::Expr(expr) => { chaining_hints(hints, sema, &famous_defs, config, file_id, &expr); + adjustment_hints(hints, sema, config, &expr); match expr { ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)), ast::Expr::MethodCallExpr(it) => { @@ -229,7 +240,7 @@ fn hints( ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, file_id, it), // We could show reborrows for all expressions, but usually that is just noise to the user // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it - ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr), + // ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr), _ => None, } }, @@ -617,30 +628,95 @@ fn closure_ret_hints( Some(()) } -fn reborrow_hints( +fn adjustment_hints( acc: &mut Vec, sema: &Semantics<'_, RootDatabase>, config: &InlayHintsConfig, expr: &ast::Expr, ) -> Option<()> { - if config.reborrow_hints == ReborrowHints::Never { + if config.adjustment_hints == AdjustmentHints::Never { + return None; + } + + if let ast::Expr::ParenExpr(_) = expr { + // These inherit from the inner expression which would result in duplicate hints return None; } + let parent = expr.syntax().parent().and_then(ast::Expr::cast); let descended = sema.descend_node_into_attributes(expr.clone()).pop(); let desc_expr = descended.as_ref().unwrap_or(expr); - let mutability = sema.is_implicit_reborrow(desc_expr)?; - let label = match mutability { - hir::Mutability::Shared if config.reborrow_hints != ReborrowHints::MutableOnly => "&*", - hir::Mutability::Mut => "&mut *", - _ => return None, + let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?; + let needs_parens = match parent { + Some(parent) => { + match parent { + ast::Expr::AwaitExpr(_) + | ast::Expr::CallExpr(_) + | ast::Expr::CastExpr(_) + | ast::Expr::FieldExpr(_) + | ast::Expr::MethodCallExpr(_) + | ast::Expr::TryExpr(_) => true, + // FIXME: shorthands need special casing, though not sure if adjustments are even valid there + ast::Expr::RecordExpr(_) => false, + ast::Expr::IndexExpr(index) => index.base().as_ref() == Some(expr), + _ => false, + } + } + None => false, }; - acc.push(InlayHint { - range: expr.syntax().text_range(), - kind: InlayKind::ImplicitReborrowHint, - label: label.to_string().into(), - tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())), - }); + if needs_parens { + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::AdjustmentHint, + label: "(".into(), + tooltip: None, + }); + } + for adjustment in adjustments.into_iter().rev() { + // FIXME: Add some nicer tooltips to each of these + let text = match adjustment { + Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => { + "" + } + Adjust::Deref(None) => "*", + Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => "*", + Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => "*", + Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => "&", + Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => "&mut ", + Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => "&raw const ", + Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => "&raw mut ", + // some of these could be represented via `as` casts, but that's not too nice and + // handling everything as a prefix expr makes the `(` and `)` insertion easier + Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => { + match cast { + PointerCast::ReifyFnPointer => "", + PointerCast::UnsafeFnPointer => "", + PointerCast::ClosureFnPointer(Safety::Unsafe) => { + "" + } + PointerCast::ClosureFnPointer(Safety::Safe) => "", + PointerCast::MutToConstPointer => "", + PointerCast::ArrayToPointer => "", + PointerCast::Unsize => "", + } + } + _ => continue, + }; + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::AdjustmentHint, + label: text.into(), + tooltip: None, + }); + } + if needs_parens { + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::AdjustmentHintClosingParenthesis, + label: ")".into(), + tooltip: None, + }); + } Some(()) } @@ -1213,12 +1289,11 @@ fn get_callable( #[cfg(test)] mod tests { use expect_test::{expect, Expect}; - use ide_db::base_db::FileRange; use itertools::Itertools; use syntax::{TextRange, TextSize}; use test_utils::extract_annotations; - use crate::inlay_hints::ReborrowHints; + use crate::inlay_hints::AdjustmentHints; use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints}; use super::ClosureReturnTypeHints; @@ -1230,7 +1305,7 @@ mod tests { chaining_hints: false, lifetime_elision_hints: LifetimeElisionHints::Never, closure_return_type_hints: ClosureReturnTypeHints::Never, - reborrow_hints: ReborrowHints::Always, + adjustment_hints: AdjustmentHints::Never, binding_mode_hints: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, @@ -1242,7 +1317,6 @@ mod tests { type_hints: true, parameter_hints: true, chaining_hints: true, - reborrow_hints: ReborrowHints::Always, closure_return_type_hints: ClosureReturnTypeHints::WithBlock, binding_mode_hints: true, lifetime_elision_hints: LifetimeElisionHints::Always, @@ -1838,10 +1912,7 @@ fn main() { .inlay_hints( &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, file_id, - Some(FileRange { - file_id, - range: TextRange::new(TextSize::from(500), TextSize::from(600)), - }), + Some(TextRange::new(TextSize::from(500), TextSize::from(600))), ) .unwrap(); let actual = @@ -2845,48 +2916,6 @@ impl () { ); } - #[test] - fn hints_implicit_reborrow() { - check_with_config( - InlayHintsConfig { - reborrow_hints: ReborrowHints::Always, - parameter_hints: true, - ..DISABLED_CONFIG - }, - r#" -fn __() { - let unique = &mut (); - let r_mov = unique; - let foo: &mut _ = unique; - //^^^^^^ &mut * - ref_mut_id(unique); - //^^^^^^ mut_ref - //^^^^^^ &mut * - let shared = ref_id(unique); - //^^^^^^ shared_ref - //^^^^^^ &* - let mov = shared; - let r_mov: &_ = shared; - ref_id(shared); - //^^^^^^ shared_ref - - identity(unique); - identity(shared); -} -fn identity(t: T) -> T { - t -} -fn ref_mut_id(mut_ref: &mut ()) -> &mut () { - mut_ref - //^^^^^^^ &mut * -} -fn ref_id(shared_ref: &()) -> &() { - shared_ref -} -"#, - ); - } - #[test] fn hints_binding_modes() { check_with_config( @@ -2994,4 +3023,76 @@ fn f() { "#, ); } + + #[test] + fn adjustment_hints() { + check_with_config( + InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, + r#" +//- minicore: coerce_unsized +fn main() { + let _: u32 = loop {}; + //^^^^^^^ + let _: &u32 = &mut 0; + //^^^^^^& + //^^^^^^* + let _: &mut u32 = &mut 0; + //^^^^^^&mut $ + //^^^^^^* + let _: *const u32 = &mut 0; + //^^^^^^&raw const $ + //^^^^^^* + let _: *mut u32 = &mut 0; + //^^^^^^&raw mut $ + //^^^^^^* + let _: fn() = main; + //^^^^ + let _: unsafe fn() = main; + //^^^^ + //^^^^ + let _: unsafe fn() = main as fn(); + //^^^^^^^^^^^^ + let _: fn() = || {}; + //^^^^^ + let _: unsafe fn() = || {}; + //^^^^^ + let _: *const u32 = &mut 0u32 as *mut u32; + //^^^^^^^^^^^^^^^^^^^^^ + let _: &mut [_] = &mut [0; 0]; + //^^^^^^^^^^^ + //^^^^^^^^^^^&mut $ + //^^^^^^^^^^^* + + Struct.consume(); + Struct.by_ref(); + //^^^^^^( + //^^^^^^& + //^^^^^^) + Struct.by_ref_mut(); + //^^^^^^( + //^^^^^^&mut $ + //^^^^^^) + + (&Struct).consume(); + //^^^^^^^* + (&Struct).by_ref(); + + (&mut Struct).consume(); + //^^^^^^^^^^^* + (&mut Struct).by_ref(); + //^^^^^^^^^^^& + //^^^^^^^^^^^* + (&mut Struct).by_ref_mut(); +} + +#[derive(Copy, Clone)] +struct Struct; +impl Struct { + fn consume(self) {} + fn by_ref(&self) {} + fn by_ref_mut(&mut self) {} +} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 416817ca0b42c..7402e86f36fa4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -81,8 +81,8 @@ pub use crate::{ highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, inlay_hints::{ - ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, - InlayTooltip, LifetimeElisionHints, ReborrowHints, + AdjustmentHints, ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, + InlayKind, InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -367,7 +367,7 @@ impl Analysis { &self, config: &InlayHintsConfig, file_id: FileId, - range: Option, + range: Option, ) -> Cancellable> { self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config)) } diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 07d117aff10bd..fcbf6d8e58c4b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -1,9 +1,9 @@ //! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports) //! for LSIF and LSP. -use hir::{db::DefDatabase, AsAssocItem, AssocItemContainer, Crate, Name, Semantics}; +use hir::{AsAssocItem, AssocItemContainer, Crate, Name, Semantics}; use ide_db::{ - base_db::{CrateOrigin, FileId, FileLoader, FilePosition, LangCrateOrigin}, + base_db::{CrateOrigin, FilePosition, LangCrateOrigin}, defs::{Definition, IdentClass}, helpers::pick_best_token, RootDatabase, @@ -11,7 +11,7 @@ use ide_db::{ use itertools::Itertools; use syntax::{AstNode, SyntaxKind::*, T}; -use crate::{doc_links::token_as_doc_comment, RangeInfo}; +use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum MonikerDescriptorKind { @@ -77,25 +77,13 @@ pub struct PackageInformation { pub version: Option, } -pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option { - for &krate in db.relevant_crates(file_id).iter() { - let crate_def_map = db.crate_def_map(krate); - for (_, data) in crate_def_map.modules() { - if data.origin.file_id() == Some(file_id) { - return Some(krate.into()); - } - } - } - None -} - pub(crate) fn moniker( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, ) -> Option>> { let sema = &Semantics::new(db); let file = sema.parse(file_id).syntax().clone(); - let current_crate = crate_for_file(db, file_id)?; + let current_crate: hir::Crate = crates_for(db, file_id).pop()?.into(); let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index e942413c11057..0f758cfa2d344 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -16,6 +16,7 @@ use ide_db::{ search::{ReferenceCategory, SearchScope, UsageSearchResult}, RootDatabase, }; +use itertools::Itertools; use stdx::hash::NoHashHashMap; use syntax::{ algo::find_node_at_offset, @@ -86,6 +87,7 @@ pub(crate) fn find_all_refs( file_id, refs.into_iter() .map(|file_ref| (file_ref.range, file_ref.category)) + .unique() .collect(), ) }) diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 7486b20293a66..e7412d27faf44 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -1345,5 +1345,36 @@ fn f i32>(f: F) { ^^ --- "#]], ); + check( + r#" +fn f &T>(f: F) { + f($0) +} +"#, + expect![[r#" + (&T, u16) -> &T + ^^ --- + "#]], + ); + } + + #[test] + fn regression_13579() { + check( + r#" +fn f() { + take(2)($0); +} + +fn take( + count: C +) -> impl Fn() -> C { + move || count +} +"#, + expect![[r#" + () -> i32 + "#]], + ); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 27ad1a948d13b..2380cf7381c1c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -13,7 +13,8 @@ use syntax::{AstNode, SyntaxKind::*, SyntaxToken, TextRange, T}; use crate::{ hover::hover_for_definition, - moniker::{crate_for_file, def_to_moniker, MonikerResult}, + moniker::{def_to_moniker, MonikerResult}, + parent_module::crates_for, Analysis, Fold, HoverConfig, HoverDocFormat, HoverResult, InlayHint, InlayHintsConfig, TryToNav, }; @@ -99,7 +100,7 @@ fn all_modules(db: &dyn HirDatabase) -> Vec { impl StaticIndex<'_> { fn add_file(&mut self, file_id: FileId) { - let current_crate = crate_for_file(self.db, file_id); + let current_crate = crates_for(self.db, file_id).pop().map(Into::into); let folds = self.analysis.folding_ranges(file_id).unwrap(); let inlay_hints = self .analysis @@ -111,7 +112,7 @@ impl StaticIndex<'_> { chaining_hints: true, closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock, lifetime_elision_hints: crate::LifetimeElisionHints::Never, - reborrow_hints: crate::ReborrowHints::Never, + adjustment_hints: crate::AdjustmentHints::Never, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, param_names_for_lifetime_elision_hints: false, diff --git a/src/tools/rust-analyzer/crates/limit/Cargo.toml b/src/tools/rust-analyzer/crates/limit/Cargo.toml index 893db436d8b73..3536f73da73e3 100644 --- a/src/tools/rust-analyzer/crates/limit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/limit/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [features] tracking = [] diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index 13cd8901031d5..bce2fc9a70e82 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs index e4c56565b92d4..cf53c16726bf7 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -12,6 +12,9 @@ use tt::buffer::{Cursor, TokenBuffer}; use crate::{to_parser_input::to_parser_input, tt_iter::TtIter, TokenMap}; +#[cfg(test)] +mod tests; + /// Convert the syntax node to a `TokenTree` (what macro /// will consume). pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> (tt::Subtree, TokenMap) { @@ -35,7 +38,7 @@ pub fn syntax_node_to_token_tree_with_modifications( append: FxHashMap>, ) -> (tt::Subtree, TokenMap, u32) { let global_offset = node.text_range().start(); - let mut c = Convertor::new(node, global_offset, existing_token_map, next_id, replace, append); + let mut c = Converter::new(node, global_offset, existing_token_map, next_id, replace, append); let subtree = convert_tokens(&mut c); c.id_alloc.map.shrink_to_fit(); always!(c.replace.is_empty(), "replace: {:?}", c.replace); @@ -100,7 +103,7 @@ pub fn parse_to_token_tree(text: &str) -> Option<(tt::Subtree, TokenMap)> { return None; } - let mut conv = RawConvertor { + let mut conv = RawConverter { lexed, pos: 0, id_alloc: TokenIdAlloc { @@ -148,7 +151,7 @@ pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec { res } -fn convert_tokens(conv: &mut C) -> tt::Subtree { +fn convert_tokens(conv: &mut C) -> tt::Subtree { struct StackEntry { subtree: tt::Subtree, idx: usize, @@ -228,7 +231,7 @@ fn convert_tokens(conv: &mut C) -> tt::Subtree { } let spacing = match conv.peek().map(|next| next.kind(conv)) { - Some(kind) if !kind.is_trivia() => tt::Spacing::Joint, + Some(kind) if is_single_token_op(kind) => tt::Spacing::Joint, _ => tt::Spacing::Alone, }; let char = match token.to_char(conv) { @@ -307,6 +310,35 @@ fn convert_tokens(conv: &mut C) -> tt::Subtree { } } +fn is_single_token_op(kind: SyntaxKind) -> bool { + matches!( + kind, + EQ | L_ANGLE + | R_ANGLE + | BANG + | AMP + | PIPE + | TILDE + | AT + | DOT + | COMMA + | SEMICOLON + | COLON + | POUND + | DOLLAR + | QUESTION + | PLUS + | MINUS + | STAR + | SLASH + | PERCENT + | CARET + // LIFETIME_IDENT will be split into a sequence of `'` (a single quote) and an + // identifier. + | LIFETIME_IDENT + ) +} + /// Returns the textual content of a doc comment block as a quoted string /// That is, strips leading `///` (or `/**`, etc) /// and strips the ending `*/` @@ -425,8 +457,8 @@ impl TokenIdAlloc { } } -/// A raw token (straight from lexer) convertor -struct RawConvertor<'a> { +/// A raw token (straight from lexer) converter +struct RawConverter<'a> { lexed: parser::LexedStr<'a>, pos: usize, id_alloc: TokenIdAlloc, @@ -442,7 +474,7 @@ trait SrcToken: std::fmt::Debug { fn synthetic_id(&self, ctx: &Ctx) -> Option; } -trait TokenConvertor: Sized { +trait TokenConverter: Sized { type Token: SrcToken; fn convert_doc_comment(&self, token: &Self::Token) -> Option>; @@ -454,25 +486,25 @@ trait TokenConvertor: Sized { fn id_alloc(&mut self) -> &mut TokenIdAlloc; } -impl<'a> SrcToken> for usize { - fn kind(&self, ctx: &RawConvertor<'a>) -> SyntaxKind { +impl<'a> SrcToken> for usize { + fn kind(&self, ctx: &RawConverter<'a>) -> SyntaxKind { ctx.lexed.kind(*self) } - fn to_char(&self, ctx: &RawConvertor<'a>) -> Option { + fn to_char(&self, ctx: &RawConverter<'a>) -> Option { ctx.lexed.text(*self).chars().next() } - fn to_text(&self, ctx: &RawConvertor<'_>) -> SmolStr { + fn to_text(&self, ctx: &RawConverter<'_>) -> SmolStr { ctx.lexed.text(*self).into() } - fn synthetic_id(&self, _ctx: &RawConvertor<'a>) -> Option { + fn synthetic_id(&self, _ctx: &RawConverter<'a>) -> Option { None } } -impl<'a> TokenConvertor for RawConvertor<'a> { +impl<'a> TokenConverter for RawConverter<'a> { type Token = usize; fn convert_doc_comment(&self, &token: &usize) -> Option> { @@ -504,7 +536,7 @@ impl<'a> TokenConvertor for RawConvertor<'a> { } } -struct Convertor { +struct Converter { id_alloc: TokenIdAlloc, current: Option, current_synthetic: Vec, @@ -515,7 +547,7 @@ struct Convertor { punct_offset: Option<(SyntaxToken, TextSize)>, } -impl Convertor { +impl Converter { fn new( node: &SyntaxNode, global_offset: TextSize, @@ -523,11 +555,11 @@ impl Convertor { next_id: u32, mut replace: FxHashMap>, mut append: FxHashMap>, - ) -> Convertor { + ) -> Converter { let range = node.text_range(); let mut preorder = node.preorder_with_tokens(); let (first, synthetic) = Self::next_token(&mut preorder, &mut replace, &mut append); - Convertor { + Converter { id_alloc: { TokenIdAlloc { map: existing_token_map, global_offset, next_id } }, current: first, current_synthetic: synthetic, @@ -590,15 +622,15 @@ impl SynToken { } } -impl SrcToken for SynToken { - fn kind(&self, _ctx: &Convertor) -> SyntaxKind { +impl SrcToken for SynToken { + fn kind(&self, ctx: &Converter) -> SyntaxKind { match self { SynToken::Ordinary(token) => token.kind(), - SynToken::Punch(token, _) => token.kind(), + SynToken::Punch(..) => SyntaxKind::from_char(self.to_char(ctx).unwrap()).unwrap(), SynToken::Synthetic(token) => token.kind, } } - fn to_char(&self, _ctx: &Convertor) -> Option { + fn to_char(&self, _ctx: &Converter) -> Option { match self { SynToken::Ordinary(_) => None, SynToken::Punch(it, i) => it.text().chars().nth((*i).into()), @@ -606,7 +638,7 @@ impl SrcToken for SynToken { SynToken::Synthetic(_) => None, } } - fn to_text(&self, _ctx: &Convertor) -> SmolStr { + fn to_text(&self, _ctx: &Converter) -> SmolStr { match self { SynToken::Ordinary(token) => token.text().into(), SynToken::Punch(token, _) => token.text().into(), @@ -614,7 +646,7 @@ impl SrcToken for SynToken { } } - fn synthetic_id(&self, _ctx: &Convertor) -> Option { + fn synthetic_id(&self, _ctx: &Converter) -> Option { match self { SynToken::Synthetic(token) => Some(token.id), _ => None, @@ -622,7 +654,7 @@ impl SrcToken for SynToken { } } -impl TokenConvertor for Convertor { +impl TokenConverter for Converter { type Token = SynToken; fn convert_doc_comment(&self, token: &Self::Token) -> Option> { convert_doc_comment(token.token()?) @@ -651,7 +683,7 @@ impl TokenConvertor for Convertor { } let curr = self.current.clone()?; - if !&self.range.contains_range(curr.text_range()) { + if !self.range.contains_range(curr.text_range()) { return None; } let (new_current, new_synth) = @@ -809,12 +841,15 @@ impl<'a> TtTreeSink<'a> { let next = last.bump(); if let ( Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(curr), _)), - Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(_), _)), + Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(next), _)), ) = (last.token_tree(), next.token_tree()) { // Note: We always assume the semi-colon would be the last token in // other parts of RA such that we don't add whitespace here. - if curr.spacing == tt::Spacing::Alone && curr.char != ';' { + // + // When `next` is a `Punct` of `'`, that's a part of a lifetime identifier so we don't + // need to add whitespace either. + if curr.spacing == tt::Spacing::Alone && curr.char != ';' && next.char != '\'' { self.inner.token(WHITESPACE, " "); self.text_pos += TextSize::of(' '); } diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs new file mode 100644 index 0000000000000..4e04d2bc1c77b --- /dev/null +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; + +use syntax::{ast, AstNode}; +use test_utils::extract_annotations; +use tt::{ + buffer::{TokenBuffer, TokenTreeRef}, + Leaf, Punct, Spacing, +}; + +use super::syntax_node_to_token_tree; + +fn check_punct_spacing(fixture: &str) { + let source_file = ast::SourceFile::parse(fixture).ok().unwrap(); + let (subtree, token_map) = syntax_node_to_token_tree(source_file.syntax()); + let mut annotations: HashMap<_, _> = extract_annotations(fixture) + .into_iter() + .map(|(range, annotation)| { + let token = token_map.token_by_range(range).expect("no token found"); + let spacing = match annotation.as_str() { + "Alone" => Spacing::Alone, + "Joint" => Spacing::Joint, + a => panic!("unknown annotation: {}", a), + }; + (token, spacing) + }) + .collect(); + + let buf = TokenBuffer::from_subtree(&subtree); + let mut cursor = buf.begin(); + while !cursor.eof() { + while let Some(token_tree) = cursor.token_tree() { + if let TokenTreeRef::Leaf(Leaf::Punct(Punct { spacing, id, .. }), _) = token_tree { + if let Some(expected) = annotations.remove(&id) { + assert_eq!(expected, *spacing); + } + } + cursor = cursor.bump_subtree(); + } + cursor = cursor.bump(); + } + + assert!(annotations.is_empty(), "unchecked annotations: {:?}", annotations); +} + +#[test] +fn punct_spacing() { + check_punct_spacing( + r#" +fn main() { + 0+0; + //^ Alone + 0+(0); + //^ Alone + 0<=0; + //^ Joint + // ^ Alone + 0<=(0); + // ^ Alone + a=0; + //^ Alone + a=(0); + //^ Alone + a+=0; + //^ Joint + // ^ Alone + a+=(0); + // ^ Alone + a&&b; + //^ Joint + // ^ Alone + a&&(b); + // ^ Alone + foo::bar; + // ^ Joint + // ^ Alone + use foo::{bar,baz,}; + // ^ Alone + // ^ Alone + // ^ Alone + struct Struct<'a> {}; + // ^ Joint + // ^ Joint + Struct::<0>; + // ^ Alone + Struct::<{0}>; + // ^ Alone + ;; + //^ Joint + // ^ Alone +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml index a286a6bcdddec..d1420de8937a0 100644 --- a/src/tools/rust-analyzer/crates/parser/Cargo.toml +++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml index 5e83de7d994e0..d23a63d2a973d 100644 --- a/src/tools/rust-analyzer/crates/paths/Cargo.toml +++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 54879c1870c05..f261f3def45d6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index 9d0da5dee9c10..7991e125ab83c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [dependencies] proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index e39026ac70bfd..a136abc12b756 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs index 2f854bc159548..0ce099ae0bab3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs @@ -117,7 +117,7 @@ impl Abi { let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?; Ok(Abi::Abi1_63(inner)) } - _ => Err(LoadProcMacroDylibError::UnsupportedABI), + _ => Err(LoadProcMacroDylibError::UnsupportedABI(info.version_string.clone())), } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 7aba74e5396de..0722cd89d7297 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -80,14 +80,14 @@ fn load_library(file: &Path) -> Result { pub enum LoadProcMacroDylibError { Io(io::Error), LibLoading(libloading::Error), - UnsupportedABI, + UnsupportedABI(String), } impl fmt::Display for LoadProcMacroDylibError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Io(e) => e.fmt(f), - Self::UnsupportedABI => write!(f, "unsupported ABI version"), + Self::UnsupportedABI(v) => write!(f, "unsupported ABI `{v}`"), Self::LibLoading(e) => e.fmt(f), } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 72a2dfe72d374..b4f5ebd157f33 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -113,12 +113,12 @@ impl ProcMacroSrv { fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> { let time = fs::metadata(path).and_then(|it| it.modified()).map_err(|err| { - format!("Failed to get file metadata for {}: {:?}", path.display(), err) + format!("Failed to get file metadata for {}: {}", path.display(), err) })?; Ok(match self.expanders.entry((path.to_path_buf(), time)) { Entry::Vacant(v) => v.insert(dylib::Expander::new(path).map_err(|err| { - format!("Cannot create expander for {}: {:?}", path.display(), err) + format!("Cannot create expander for {}: {}", path.display(), err) })?), Entry::Occupied(e) => e.into_mut(), }) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index b46cdddcf6b10..cc0fc91fe989e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -19,7 +19,7 @@ fn test_derive_error() { expect![[r##" SUBTREE $ IDENT compile_error 4294967295 - PUNCH ! [joint] 4294967295 + PUNCH ! [alone] 4294967295 SUBTREE () 4294967295 LITERAL "#[derive(DeriveError)] struct S ;" 4294967295 PUNCH ; [alone] 4294967295"##]], @@ -109,7 +109,7 @@ fn test_fn_like_macro_clone_literals() { PUNCH , [alone] 4294967295 LITERAL 2_u32 4294967295 PUNCH , [alone] 4294967295 - PUNCH - [joint] 4294967295 + PUNCH - [alone] 4294967295 LITERAL 4i64 4294967295 PUNCH , [alone] 4294967295 LITERAL 3.14f32 4294967295 @@ -130,7 +130,7 @@ fn test_attr_macro() { expect![[r##" SUBTREE $ IDENT compile_error 4294967295 - PUNCH ! [joint] 4294967295 + PUNCH ! [alone] 4294967295 SUBTREE () 4294967295 LITERAL "#[attr_error(some arguments)] mod m {}" 4294967295 PUNCH ; [alone] 4294967295"##]], diff --git a/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml index 684477191b276..d2a79f91074a1 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml @@ -3,7 +3,7 @@ name = "proc-macro-test" version = "0.0.0" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" publish = false [lib] diff --git a/src/tools/rust-analyzer/crates/proc-macro-test/imp/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-test/imp/Cargo.toml index 2d1fc3c5c7a3b..1bd14070e90da 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-test/imp/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-test/imp/Cargo.toml @@ -3,7 +3,7 @@ name = "proc-macro-test-impl" version = "0.0.0" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" publish = false [lib] diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 5697aea964f78..01d1735bf7843 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index cf9868740cb03..39902a53214d0 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index a26a7c57acfce..ae2b41f27d58e 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs @@ -69,7 +69,7 @@ impl WorkspaceBuildScripts { cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]); // --all-targets includes tests, benches and examples in addition to the - // default lib and bins. This is an independent concept from the --targets + // default lib and bins. This is an independent concept from the --target // flag below. cmd.arg("--all-targets"); diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index b4c2ba436772f..02ec7a4f6f992 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -270,11 +270,7 @@ impl CargoWorkspace { config: &CargoConfig, progress: &dyn Fn(String), ) -> Result { - let target = config - .target - .clone() - .or_else(|| cargo_config_build_target(cargo_toml, &config.extra_env)) - .or_else(|| rustc_discover_host_triple(cargo_toml, &config.extra_env)); + let targets = find_list_of_build_targets(config, cargo_toml); let mut meta = MetadataCommand::new(); meta.cargo_path(toolchain::cargo()); @@ -294,8 +290,12 @@ impl CargoWorkspace { } meta.current_dir(current_dir.as_os_str()); - if let Some(target) = target { - meta.other_options(vec![String::from("--filter-platform"), target]); + if !targets.is_empty() { + let other_options: Vec<_> = targets + .into_iter() + .flat_map(|target| ["--filter-platform".to_string(), target]) + .collect(); + meta.other_options(other_options); } // FIXME: Fetching metadata is a slow process, as it might require @@ -469,6 +469,19 @@ impl CargoWorkspace { } } +fn find_list_of_build_targets(config: &CargoConfig, cargo_toml: &ManifestPath) -> Vec { + if let Some(target) = &config.target { + return [target.into()].to_vec(); + } + + let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env); + if !build_targets.is_empty() { + return build_targets; + } + + rustc_discover_host_triple(cargo_toml, &config.extra_env).into_iter().collect() +} + fn rustc_discover_host_triple( cargo_toml: &ManifestPath, extra_env: &FxHashMap, @@ -499,7 +512,7 @@ fn rustc_discover_host_triple( fn cargo_config_build_target( cargo_toml: &ManifestPath, extra_env: &FxHashMap, -) -> Option { +) -> Vec { let mut cargo_config = Command::new(toolchain::cargo()); cargo_config.envs(extra_env); cargo_config @@ -507,12 +520,21 @@ fn cargo_config_build_target( .args(&["-Z", "unstable-options", "config", "get", "build.target"]) .env("RUSTC_BOOTSTRAP", "1"); // if successful we receive `build.target = "target-triple"` + // or `build.target = ["", ..]` tracing::debug!("Discovering cargo config target by {:?}", cargo_config); - match utf8_stdout(cargo_config) { - Ok(stdout) => stdout - .strip_prefix("build.target = \"") - .and_then(|stdout| stdout.strip_suffix('"')) - .map(ToOwned::to_owned), - Err(_) => None, + utf8_stdout(cargo_config).map(parse_output_cargo_config_build_target).unwrap_or_default() +} + +fn parse_output_cargo_config_build_target(stdout: String) -> Vec { + let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"'); + + if !trimmed.starts_with('[') { + return [trimmed.to_string()].to_vec(); + } + + let res = serde_json::from_str(trimmed); + if let Err(e) = &res { + tracing::warn!("Failed to parse `build.target` as an array of target: {}`", e); } + res.unwrap_or_default() } diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index fa8d76f3f4529..f6c09a27c9d7e 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -128,14 +128,18 @@ impl Sysroot { } if let Some(alloc) = sysroot.by_name("alloc") { - if let Some(core) = sysroot.by_name("core") { - sysroot.crates[alloc].deps.push(core); + for dep in ALLOC_DEPS.trim().lines() { + if let Some(dep) = sysroot.by_name(dep) { + sysroot.crates[alloc].deps.push(dep) + } } } if let Some(proc_macro) = sysroot.by_name("proc_macro") { - if let Some(std) = sysroot.by_name("std") { - sysroot.crates[proc_macro].deps.push(std); + for dep in PROC_MACRO_DEPS.trim().lines() { + if let Some(dep) = sysroot.by_name(dep) { + sysroot.crates[proc_macro].deps.push(dep) + } } } @@ -239,6 +243,7 @@ fn get_rust_src(sysroot_path: &AbsPath) -> Option { const SYSROOT_CRATES: &str = " alloc +backtrace core panic_abort panic_unwind @@ -246,17 +251,19 @@ proc_macro profiler_builtins std stdarch/crates/std_detect -term test unwind"; +const ALLOC_DEPS: &str = "core"; + const STD_DEPS: &str = " alloc -core -panic_abort panic_unwind +panic_abort +core profiler_builtins +unwind std_detect -term -test -unwind"; +test"; + +const PROC_MACRO_DEPS: &str = "std"; 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 e2444e24974a7..a1cb438bddc4c 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -1566,10 +1566,10 @@ fn rust_project_hello_world_project_model() { }, Dependency { crate_id: CrateId( - 1, + 3, ), name: CrateName( - "core", + "panic_unwind", ), prelude: true, }, @@ -1584,10 +1584,10 @@ fn rust_project_hello_world_project_model() { }, Dependency { crate_id: CrateId( - 3, + 1, ), name: CrateName( - "panic_unwind", + "core", ), prelude: true, }, @@ -1602,40 +1602,31 @@ fn rust_project_hello_world_project_model() { }, Dependency { crate_id: CrateId( - 7, + 9, ), name: CrateName( - "std_detect", + "unwind", ), prelude: true, }, Dependency { crate_id: CrateId( - 8, + 7, ), name: CrateName( - "term", + "std_detect", ), prelude: true, }, Dependency { crate_id: CrateId( - 9, + 8, ), name: CrateName( "test", ), prelude: true, }, - Dependency { - crate_id: CrateId( - 10, - ), - name: CrateName( - "unwind", - ), - prelude: true, - }, ], proc_macro: Err( "no proc macro loaded for sysroot crate", @@ -1687,40 +1678,6 @@ fn rust_project_hello_world_project_model() { ), edition: Edition2018, version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "term", - ), - canonical_name: "term", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - env: Env { - entries: {}, - }, - dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Other, - ), - is_proc_macro: false, - }, - CrateId( - 9, - ): CrateData { - root_file_id: FileId( - 10, - ), - edition: Edition2018, - version: None, display_name: Some( CrateDisplayName { crate_name: CrateName( @@ -1748,10 +1705,10 @@ fn rust_project_hello_world_project_model() { is_proc_macro: false, }, CrateId( - 10, + 9, ): CrateData { root_file_id: FileId( - 11, + 10, ), edition: Edition2018, version: None, @@ -1782,10 +1739,10 @@ fn rust_project_hello_world_project_model() { is_proc_macro: false, }, CrateId( - 11, + 10, ): CrateData { root_file_id: FileId( - 12, + 11, ), edition: Edition2018, version: None, @@ -1836,7 +1793,7 @@ fn rust_project_hello_world_project_model() { }, Dependency { crate_id: CrateId( - 9, + 8, ), name: CrateName( "test", diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 2780c62ed118a..3d199ed24afe7 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -377,6 +377,21 @@ impl ProjectWorkspace { } } + pub fn find_sysroot_proc_macro_srv(&self) -> Option { + match self { + ProjectWorkspace::Cargo { sysroot: Some(sysroot), .. } + | ProjectWorkspace::Json { sysroot: Some(sysroot), .. } => { + let standalone_server_name = + format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX); + ["libexec", "lib"] + .into_iter() + .map(|segment| sysroot.root().join(segment).join(&standalone_server_name)) + .find(|server_path| std::fs::metadata(&server_path).is_ok()) + } + _ => None, + } + } + /// Returns the roots for the current `ProjectWorkspace` /// The return type contains the path and whether or not /// the root is a member of the current workspace @@ -509,14 +524,14 @@ impl ProjectWorkspace { build_scripts, toolchain: _, } => cargo_to_crate_graph( - rustc_cfg.clone(), - cfg_overrides, load_proc_macro, load, + rustc, cargo, - build_scripts, sysroot.as_ref(), - rustc, + rustc_cfg.clone(), + cfg_overrides, + build_scripts, ), ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => { detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot) @@ -602,7 +617,7 @@ fn project_json_to_crate_graph( for (from, krate) in project.crates() { if let Some(&from) = crates.get(&from) { if let Some((public_deps, libproc_macro)) = &sysroot_deps { - public_deps.add(from, &mut crate_graph); + public_deps.add_to_crate_graph(&mut crate_graph, from); if krate.is_proc_macro { if let Some(proc_macro) = libproc_macro { add_dep( @@ -626,14 +641,14 @@ fn project_json_to_crate_graph( } fn cargo_to_crate_graph( - rustc_cfg: Vec, - override_cfg: &CfgOverrides, load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, + rustc: &Option, cargo: &CargoWorkspace, - build_scripts: &WorkspaceBuildScripts, sysroot: Option<&Sysroot>, - rustc: &Option, + rustc_cfg: Vec, + override_cfg: &CfgOverrides, + build_scripts: &WorkspaceBuildScripts, ) -> CrateGraph { let _p = profile::span("cargo_to_crate_graph"); let mut crate_graph = CrateGraph::default(); @@ -642,13 +657,15 @@ fn cargo_to_crate_graph( None => (SysrootPublicDeps::default(), None), }; - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg); + let cfg_options = { + let mut cfg_options = CfgOptions::default(); + cfg_options.extend(rustc_cfg); + cfg_options.insert_atom("debug_assertions".into()); + cfg_options + }; let mut pkg_to_lib_crate = FxHashMap::default(); - cfg_options.insert_atom("debug_assertions".into()); - let mut pkg_crates = FxHashMap::default(); // Does any crate signal to rust-analyzer that they need the rustc_private crates? let mut has_private = false; @@ -723,7 +740,7 @@ fn cargo_to_crate_graph( // Set deps to the core, std and to the lib target of the current package for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() { // Add sysroot deps first so that a lib target named `core` etc. can overwrite them. - public_deps.add(from, &mut crate_graph); + public_deps.add_to_crate_graph(&mut crate_graph, from); if let Some((to, name)) = lib_tgt.clone() { if to != from && kind != TargetKind::BuildScript { @@ -767,15 +784,16 @@ fn cargo_to_crate_graph( if let Some(rustc_workspace) = rustc { handle_rustc_crates( &mut crate_graph, - rustc_workspace, + &mut pkg_to_lib_crate, load, - &cfg_options, - override_cfg, load_proc_macro, - &mut pkg_to_lib_crate, - &public_deps, + rustc_workspace, cargo, + &public_deps, + libproc_macro, &pkg_crates, + &cfg_options, + override_cfg, build_scripts, ); } @@ -825,28 +843,29 @@ fn detached_files_to_crate_graph( }, ); - public_deps.add(detached_file_crate, &mut crate_graph); + public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate); } crate_graph } fn handle_rustc_crates( crate_graph: &mut CrateGraph, - rustc_workspace: &CargoWorkspace, + pkg_to_lib_crate: &mut FxHashMap, load: &mut dyn FnMut(&AbsPath) -> Option, - cfg_options: &CfgOptions, - override_cfg: &CfgOverrides, load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, - pkg_to_lib_crate: &mut FxHashMap, - public_deps: &SysrootPublicDeps, + rustc_workspace: &CargoWorkspace, cargo: &CargoWorkspace, + public_deps: &SysrootPublicDeps, + libproc_macro: Option, pkg_crates: &FxHashMap>, + cfg_options: &CfgOptions, + override_cfg: &CfgOverrides, build_scripts: &WorkspaceBuildScripts, ) { let mut rustc_pkg_crates = FxHashMap::default(); // The root package of the rustc-dev component is rustc_driver, so we match that let root_pkg = - rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver"); + rustc_workspace.packages().find(|&package| rustc_workspace[package].name == "rustc_driver"); // The rustc workspace might be incomplete (such as if rustc-dev is not // installed for the current toolchain) and `rustc_source` is set to discover. if let Some(root_pkg) = root_pkg { @@ -901,7 +920,16 @@ fn handle_rustc_crates( ); pkg_to_lib_crate.insert(pkg, crate_id); // Add dependencies on core / std / alloc for this crate - public_deps.add(crate_id, crate_graph); + public_deps.add_to_crate_graph(crate_graph, crate_id); + if let Some(proc_macro) = libproc_macro { + add_dep_with_prelude( + crate_graph, + crate_id, + CrateName::new("proc_macro").unwrap(), + proc_macro, + rustc_workspace[tgt].is_proc_macro, + ); + } rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); } } @@ -1009,7 +1037,7 @@ struct SysrootPublicDeps { impl SysrootPublicDeps { /// Makes `from` depend on the public sysroot crates. - fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) { + fn add_to_crate_graph(&self, crate_graph: &mut CrateGraph, from: CrateId) { for (name, krate, prelude) in &self.deps { add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 7ae5324ab051a..56f14fe187490 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -8,7 +8,7 @@ documentation = "https://rust-analyzer.github.io/manual.html" license = "MIT OR Apache-2.0" autobins = false edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs index 6ede194babc20..cf51cf15a0e1d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs @@ -3,11 +3,11 @@ use std::mem; use cfg::{CfgAtom, CfgExpr}; -use ide::{FileId, RunnableKind, TestId}; +use ide::{Cancellable, FileId, RunnableKind, TestId}; use project_model::{self, CargoFeatures, ManifestPath, TargetKind}; use vfs::AbsPathBuf; -use crate::{global_state::GlobalStateSnapshot, Result}; +use crate::global_state::GlobalStateSnapshot; /// Abstract representation of Cargo target. /// @@ -29,7 +29,7 @@ impl CargoTargetSpec { spec: Option, kind: &RunnableKind, cfg: &Option, - ) -> Result<(Vec, Vec)> { + ) -> (Vec, Vec) { let mut args = Vec::new(); let mut extra_args = Vec::new(); @@ -111,13 +111,13 @@ impl CargoTargetSpec { } } } - Ok((args, extra_args)) + (args, extra_args) } pub(crate) fn for_file( global_state_snapshot: &GlobalStateSnapshot, file_id: FileId, - ) -> Result> { + ) -> Cancellable> { let crate_id = match &*global_state_snapshot.analysis.crates_for(file_id)? { &[crate_id, ..] => crate_id, _ => return Ok(None), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs index 5dba545b87184..762d7d3a18e8b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs @@ -60,24 +60,12 @@ pub fn load_workspace( }; let proc_macro_client = if load_config.with_proc_macro { - let mut path = AbsPathBuf::assert(std::env::current_exe()?); - let mut args = vec!["proc-macro"]; - - if let ProjectWorkspace::Cargo { sysroot, .. } | ProjectWorkspace::Json { sysroot, .. } = - &ws - { - if let Some(sysroot) = sysroot.as_ref() { - let standalone_server_name = - format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX); - let server_path = sysroot.root().join("libexec").join(&standalone_server_name); - if std::fs::metadata(&server_path).is_ok() { - path = server_path; - args = vec![]; - } - } - } + let (server_path, args): (_, &[_]) = match ws.find_sysroot_proc_macro_srv() { + Some(server_path) => (server_path, &[]), + None => (AbsPathBuf::assert(std::env::current_exe()?), &["proc-macro"]), + }; - ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|e| e.to_string()) + ProcMacroServer::spawn(server_path, args).map_err(|e| e.to_string()) } else { Err("proc macro server disabled".to_owned()) }; 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 ca7ba896b67cf..9edd045ab0716 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 @@ -47,30 +47,27 @@ impl flags::Scip { let si = StaticIndex::compute(&analysis); - let mut index = scip_types::Index { - metadata: Some(scip_types::Metadata { - version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(), - tool_info: Some(scip_types::ToolInfo { - name: "rust-analyzer".to_owned(), - version: "0.1".to_owned(), - arguments: vec![], - ..Default::default() - }) - .into(), - project_root: format!( - "file://{}", - path.normalize() - .as_os_str() - .to_str() - .ok_or(anyhow::anyhow!("Unable to normalize project_root path"))? - .to_string() - ), - text_document_encoding: scip_types::TextEncoding::UTF8.into(), - ..Default::default() + let metadata = scip_types::Metadata { + version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(), + tool_info: Some(scip_types::ToolInfo { + name: "rust-analyzer".to_owned(), + version: "0.1".to_owned(), + arguments: vec![], + special_fields: Default::default(), }) .into(), - ..Default::default() + project_root: format!( + "file://{}", + path.normalize() + .as_os_str() + .to_str() + .ok_or(anyhow::anyhow!("Unable to normalize project_root path"))? + .to_string() + ), + text_document_encoding: scip_types::TextEncoding::UTF8.into(), + special_fields: Default::default(), }; + let mut documents = Vec::new(); let mut symbols_emitted: HashSet = HashSet::default(); let mut tokens_to_symbol: HashMap = HashMap::new(); @@ -95,18 +92,14 @@ impl flags::Scip { endings: LineEndings::Unix, }; - let mut doc = scip_types::Document { - relative_path, - language: "rust".to_string(), - ..Default::default() - }; + let mut occurrences = Vec::new(); + let mut symbols = Vec::new(); - tokens.into_iter().for_each(|(range, id)| { + tokens.into_iter().for_each(|(text_range, id)| { let token = si.tokens.get(id).unwrap(); - let mut occurrence = scip_types::Occurrence::default(); - occurrence.range = text_range_to_scip_range(&line_index, range); - occurrence.symbol = tokens_to_symbol + let range = text_range_to_scip_range(&line_index, text_range); + let symbol = tokens_to_symbol .entry(id) .or_insert_with(|| { let symbol = token_to_symbol(&token).unwrap_or_else(&mut new_local_symbol); @@ -114,34 +107,62 @@ impl flags::Scip { }) .clone(); + let mut symbol_roles = Default::default(); + if let Some(def) = token.definition { - if def.range == range { - occurrence.symbol_roles |= scip_types::SymbolRole::Definition as i32; + if def.range == text_range { + symbol_roles |= scip_types::SymbolRole::Definition as i32; } if symbols_emitted.insert(id) { - let mut symbol_info = scip_types::SymbolInformation::default(); - symbol_info.symbol = occurrence.symbol.clone(); - if let Some(hover) = &token.hover { - if !hover.markup.as_str().is_empty() { - symbol_info.documentation = vec![hover.markup.as_str().to_string()]; - } - } - - doc.symbols.push(symbol_info) + let documentation = token + .hover + .as_ref() + .map(|hover| hover.markup.as_str()) + .filter(|it| !it.is_empty()) + .map(|it| vec![it.to_owned()]); + let symbol_info = scip_types::SymbolInformation { + symbol: symbol.clone(), + documentation: documentation.unwrap_or_default(), + relationships: Vec::new(), + special_fields: Default::default(), + }; + + symbols.push(symbol_info) } } - doc.occurrences.push(occurrence); + occurrences.push(scip_types::Occurrence { + range, + symbol, + symbol_roles, + override_documentation: Vec::new(), + syntax_kind: Default::default(), + diagnostics: Vec::new(), + special_fields: Default::default(), + }); }); - if doc.occurrences.is_empty() { + if occurrences.is_empty() { continue; } - index.documents.push(doc); + documents.push(scip_types::Document { + relative_path, + language: "rust".to_string(), + occurrences, + symbols, + special_fields: Default::default(), + }); } + let index = scip_types::Index { + metadata: Some(metadata).into(), + documents, + external_symbols: Vec::new(), + special_fields: Default::default(), + }; + scip::write_message_to_file("index.scip", index) .map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?; @@ -181,7 +202,7 @@ fn new_descriptor_str( name: name.to_string(), disambiguator: "".to_string(), suffix: suffix.into(), - ..Default::default() + special_fields: Default::default(), } } @@ -232,11 +253,11 @@ fn token_to_symbol(token: &TokenStaticData) -> Option { manager: "cargo".to_string(), name: package_name, version: version.unwrap_or_else(|| ".".to_string()), - ..Default::default() + special_fields: Default::default(), }) .into(), descriptors, - ..Default::default() + special_fields: Default::default(), }) } 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 4072ae585dbd9..6b2f22faa7178 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -118,9 +118,11 @@ config_data! { /// This option does not take effect until rust-analyzer is restarted. cargo_sysroot: Option = "\"discover\"", /// Compilation target override (target triple). + // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work + // than `checkOnSave_target` cargo_target: Option = "null", /// Unsets `#[cfg(test)]` for the specified crates. - cargo_unsetTest: Vec = "[\"core\"]", + cargo_unsetTest: Vec = "[\"core\"]", /// Check all targets and tests (`--all-targets`). checkOnSave_allTargets: bool = "true", @@ -157,7 +159,7 @@ config_data! { checkOnSave_noDefaultFeatures: Option = "null", /// Override the command rust-analyzer uses instead of `cargo check` for /// diagnostics on save. The command is required to output json and - /// should therefor include `--message-format=json` or a similar option. + /// should therefore include `--message-format=json` or a similar option. /// /// If you're changing this because you're using some tool wrapping /// Cargo, you might also want to change @@ -174,9 +176,13 @@ config_data! { /// ``` /// . checkOnSave_overrideCommand: Option> = "null", - /// Check for a specific target. Defaults to - /// `#rust-analyzer.cargo.target#`. - checkOnSave_target: Option = "null", + /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. + /// + /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. + /// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`. + /// + /// Aliased as `"checkOnSave.targets"`. + checkOnSave_target | checkOnSave_targets: CheckOnSaveTargets = "[]", /// Toggles the additional completions that automatically add imports when completed. /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. @@ -261,6 +267,7 @@ config_data! { files_excludeDirs: Vec = "[]", /// Controls file watching implementation. files_watcher: FilesWatcherDef = "\"client\"", + /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. highlightRelated_breakPoints_enable: bool = "true", /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). @@ -320,6 +327,8 @@ config_data! { inlayHints_closingBraceHints_minLines: usize = "25", /// Whether to show inlay type hints for return types of closures. inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = "\"never\"", + /// Whether to show inlay hints for type adjustments. + inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"", /// Whether to show inlay type hints for elided lifetimes in function signatures. inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"", /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. @@ -329,7 +338,8 @@ config_data! { /// Whether to show function parameter name inlay hints at the call /// site. inlayHints_parameterHints_enable: bool = "true", - /// Whether to show inlay type hints for compiler inserted reborrows. + /// Whether to show inlay hints for compiler inserted reborrows. + /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. inlayHints_reborrowHints_enable: ReborrowHintsDef = "\"never\"", /// Whether to render leading colons for type hints, and trailing colons for parameter hints. inlayHints_renderColons: bool = "true", @@ -1143,11 +1153,10 @@ impl Config { } Some(_) | None => FlycheckConfig::CargoCommand { command: self.data.checkOnSave_command.clone(), - target_triple: self - .data - .checkOnSave_target - .clone() - .or_else(|| self.data.cargo_target.clone()), + target_triples: match &self.data.checkOnSave_target.0[..] { + [] => self.data.cargo_target.clone().into_iter().collect(), + targets => targets.into(), + }, all_targets: self.data.checkOnSave_allTargets, no_default_features: self .data @@ -1200,10 +1209,15 @@ impl Config { hide_closure_initialization_hints: self .data .inlayHints_typeHints_hideClosureInitialization, - reborrow_hints: match self.data.inlayHints_reborrowHints_enable { - ReborrowHintsDef::Always => ide::ReborrowHints::Always, - ReborrowHintsDef::Never => ide::ReborrowHints::Never, - ReborrowHintsDef::Mutable => ide::ReborrowHints::MutableOnly, + adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable { + AdjustmentHintsDef::Always => ide::AdjustmentHints::Always, + AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable { + ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => { + ide::AdjustmentHints::ReborrowOnly + } + ReborrowHintsDef::Never => ide::AdjustmentHints::Never, + }, + AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly, }, binding_mode_hints: self.data.inlayHints_bindingModeHints_enable, param_names_for_lifetime_elision_hints: self @@ -1538,6 +1552,7 @@ mod de_unit_v { named_unit_variant!(all); named_unit_variant!(skip_trivial); named_unit_variant!(mutable); + named_unit_variant!(reborrow); named_unit_variant!(with_block); } @@ -1647,6 +1662,9 @@ enum InvocationStrategy { PerWorkspace, } +#[derive(Deserialize, Debug, Clone)] +struct CheckOnSaveTargets(#[serde(deserialize_with = "single_or_array")] Vec); + #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum InvocationLocation { @@ -1687,6 +1705,17 @@ enum ReborrowHintsDef { Mutable, } +#[derive(Deserialize, Debug, Clone)] +#[serde(untagged)] +enum AdjustmentHintsDef { + #[serde(deserialize_with = "true_or_always")] + Always, + #[serde(deserialize_with = "false_or_never")] + Never, + #[serde(deserialize_with = "de_unit_v::reborrow")] + Reborrow, +} + #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum FilesWatcherDef { @@ -1996,6 +2025,19 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "Only show mutable reborrow hints." ] }, + "AdjustmentHintsDef" => set! { + "type": "string", + "enum": [ + "always", + "never", + "reborrow" + ], + "enumDescriptions": [ + "Always show all adjustment hints.", + "Never show adjustment hints.", + "Only show auto borrow and dereference adjustment hints." + ] + }, "CargoFeaturesDef" => set! { "anyOf": [ { @@ -2084,6 +2126,17 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "The command will be executed in the project root." ], }, + "CheckOnSaveTargets" => set! { + "anyOf": [ + { + "type": "string", + }, + { + "type": "array", + "items": { "type": "string" } + }, + ], + }, _ => panic!("missing entry for {}: {}", ty, default), } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs index 189ac2fbf5339..beb23c54c9f0f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -359,14 +359,15 @@ pub(crate) fn map_rust_diagnostic_to_lsp( .iter() .flat_map(|primary_span| { let primary_location = primary_location(config, workspace_root, primary_span, snap); - - let mut message = message.clone(); - if needs_primary_span_label { - if let Some(primary_span_label) = &primary_span.label { - format_to!(message, "\n{}", primary_span_label); + let message = { + let mut message = message.clone(); + if needs_primary_span_label { + if let Some(primary_span_label) = &primary_span.label { + format_to!(message, "\n{}", primary_span_label); + } } - } - + message + }; // Each primary diagnostic span may result in multiple LSP diagnostics. let mut diagnostics = Vec::new(); @@ -417,7 +418,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( message: message.clone(), related_information: Some(information_for_additional_diagnostic), tags: if tags.is_empty() { None } else { Some(tags.clone()) }, - data: None, + data: Some(serde_json::json!({ "rendered": rd.rendered })), }; diagnostics.push(MappedRustDiagnostic { url: secondary_location.uri, @@ -449,7 +450,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( } }, tags: if tags.is_empty() { None } else { Some(tags.clone()) }, - data: None, + data: Some(serde_json::json!({ "rendered": rd.rendered })), }, fix: None, }); @@ -534,7 +535,8 @@ mod tests { Config::new(workspace_root.to_path_buf(), ClientCapabilities::default()), ); let snap = state.snapshot(); - let actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap); + let mut actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap); + actual.iter_mut().for_each(|diag| diag.diagnostic.data = None); expect.assert_debug_eq(&actual) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs index 936957bab4882..dd433b0f4d31c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs @@ -42,8 +42,10 @@ pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> R pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Result { let start = offset(line_index, range.start)?; let end = offset(line_index, range.end)?; - let text_range = TextRange::new(start, end); - Ok(text_range) + match end < start { + true => Err(format_err!("Invalid Range").into()), + false => Ok(TextRange::new(start, end)), + } } pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 74277ff2e576e..4e8bc8d6462ce 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -100,7 +100,7 @@ pub(crate) struct GlobalState { /// the user just adds comments or whitespace to Cargo.toml, we do not want /// to invalidate any salsa caches. pub(crate) workspaces: Arc>, - pub(crate) fetch_workspaces_queue: OpQueue>>, + pub(crate) fetch_workspaces_queue: OpQueue>>>, pub(crate) fetch_build_data_queue: OpQueue<(Arc>, Vec>)>, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs index 34795a8eb40ab..d190a9f4e2ca9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs @@ -9,9 +9,9 @@ use std::{ use anyhow::Context; use ide::{ - AnnotationConfig, AssistKind, AssistResolveStrategy, FileId, FilePosition, FileRange, - HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, - SingleResolve, SourceChange, TextEdit, + AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FileId, FilePosition, + FileRange, HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, + RunnableKind, SingleResolve, SourceChange, TextEdit, }; use ide_db::SymbolKind; use lsp_server::ErrorCode; @@ -556,7 +556,7 @@ pub(crate) fn handle_will_rename_files( if source_change.source_file_edits.is_empty() { Ok(None) } else { - to_proto::workspace_edit(&snap, source_change).map(Some) + Ok(Some(to_proto::workspace_edit(&snap, source_change)?)) } } @@ -1313,7 +1313,7 @@ pub(crate) fn handle_ssr( position, selections, )??; - to_proto::workspace_edit(&snap, source_change) + to_proto::workspace_edit(&snap, source_change).map_err(Into::into) } pub(crate) fn publish_diagnostics( @@ -1354,13 +1354,12 @@ pub(crate) fn handle_inlay_hints( ) -> Result>> { let _p = profile::span("handle_inlay_hints"); let document_uri = ¶ms.text_document.uri; - let file_id = from_proto::file_id(&snap, document_uri)?; - let line_index = snap.file_line_index(file_id)?; - let range = from_proto::file_range( + let FileRange { file_id, range } = from_proto::file_range( &snap, TextDocumentIdentifier::new(document_uri.to_owned()), params.range, )?; + let line_index = snap.file_line_index(file_id)?; let inlay_hints_config = snap.config.inlay_hints(); Ok(Some( snap.analysis @@ -1369,7 +1368,7 @@ pub(crate) fn handle_inlay_hints( .map(|it| { to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it) }) - .collect::>>()?, + .collect::>>()?, )) } @@ -1426,7 +1425,7 @@ pub(crate) fn handle_call_hierarchy_prepare( .into_iter() .filter(|it| it.kind == Some(SymbolKind::Function)) .map(|it| to_proto::call_hierarchy_item(&snap, it)) - .collect::>>()?; + .collect::>>()?; Ok(Some(res)) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs index 0d424b915703a..2945dba12f255 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs @@ -27,10 +27,6 @@ pub(crate) enum LineEndings { impl LineEndings { /// Replaces `\r\n` with `\n` in-place in `src`. pub(crate) fn normalize(src: String) -> (String, LineEndings) { - if !src.as_bytes().contains(&b'\r') { - return (src, LineEndings::Unix); - } - // We replace `\r\n` with `\n` in-place, which doesn't break utf-8 encoding. // While we *can* call `as_mut_vec` and do surgery on the live string // directly, let's rather steal the contents of `src`. This makes the code @@ -39,10 +35,19 @@ impl LineEndings { let mut buf = src.into_bytes(); let mut gap_len = 0; let mut tail = buf.as_mut_slice(); + let mut crlf_seen = false; + + let find_crlf = |src: &[u8]| src.windows(2).position(|it| it == b"\r\n"); + loop { let idx = match find_crlf(&tail[gap_len..]) { - None => tail.len(), - Some(idx) => idx + gap_len, + None if crlf_seen => tail.len(), + // SAFETY: buf is unchanged and therefore still contains utf8 data + None => return (unsafe { String::from_utf8_unchecked(buf) }, LineEndings::Unix), + Some(idx) => { + crlf_seen = true; + idx + gap_len + } }; tail.copy_within(gap_len..idx, 0); tail = &mut tail[idx - gap_len..]; @@ -54,15 +59,48 @@ impl LineEndings { // Account for removed `\r`. // After `set_len`, `buf` is guaranteed to contain utf-8 again. - let new_len = buf.len() - gap_len; let src = unsafe { + let new_len = buf.len() - gap_len; buf.set_len(new_len); String::from_utf8_unchecked(buf) }; - return (src, LineEndings::Dos); + (src, LineEndings::Dos) + } +} - fn find_crlf(src: &[u8]) -> Option { - src.windows(2).position(|it| it == b"\r\n") - } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn unix() { + let src = "a\nb\nc\n\n\n\n"; + let (res, endings) = LineEndings::normalize(src.into()); + assert_eq!(endings, LineEndings::Unix); + assert_eq!(res, src); + } + + #[test] + fn dos() { + let src = "\r\na\r\n\r\nb\r\nc\r\n\r\n\r\n\r\n"; + let (res, endings) = LineEndings::normalize(src.into()); + assert_eq!(endings, LineEndings::Dos); + assert_eq!(res, "\na\n\nb\nc\n\n\n\n"); + } + + #[test] + fn mixed() { + let src = "a\r\nb\r\nc\r\n\n\r\n\n"; + let (res, endings) = LineEndings::normalize(src.into()); + assert_eq!(endings, LineEndings::Dos); + assert_eq!(res, "a\nb\nc\n\n\n\n"); + } + + #[test] + fn none() { + let src = "abc"; + let (res, endings) = LineEndings::normalize(src.into()); + assert_eq!(endings, LineEndings::Unix); + assert_eq!(res, src); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs index c6a4db9a453ac..0971dc36f3a5c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs @@ -1,5 +1,5 @@ //! Utilities for LSP-related boilerplate code. -use std::{ops::Range, sync::Arc}; +use std::{mem, ops::Range, sync::Arc}; use lsp_server::Notification; @@ -133,11 +133,37 @@ impl GlobalState { } pub(crate) fn apply_document_changes( - old_text: &mut String, - content_changes: Vec, -) { + file_contents: impl FnOnce() -> String, + mut content_changes: Vec, +) -> String { + // Skip to the last full document change, as it invalidates all previous changes anyways. + let mut start = content_changes + .iter() + .rev() + .position(|change| change.range.is_none()) + .map(|idx| content_changes.len() - idx - 1) + .unwrap_or(0); + + let mut text: String = match content_changes.get_mut(start) { + // peek at the first content change as an optimization + Some(lsp_types::TextDocumentContentChangeEvent { range: None, text, .. }) => { + let text = mem::take(text); + start += 1; + + // The only change is a full document update + if start == content_changes.len() { + return text; + } + text + } + Some(_) => file_contents(), + // we received no content changes + None => return file_contents(), + }; + let mut line_index = LineIndex { - index: Arc::new(ide::LineIndex::new(old_text)), + // the index will be overwritten in the bottom loop's first iteration + index: Arc::new(ide::LineIndex::new(&text)), // We don't care about line endings or offset encoding here. endings: LineEndings::Unix, encoding: PositionEncoding::Utf16, @@ -148,38 +174,20 @@ pub(crate) fn apply_document_changes( // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we // remember the last valid line in the index and only rebuild it if needed. // The VFS will normalize the end of lines to `\n`. - enum IndexValid { - All, - UpToLineExclusive(u32), - } - - impl IndexValid { - fn covers(&self, line: u32) -> bool { - match *self { - IndexValid::UpToLineExclusive(to) => to > line, - _ => true, - } - } - } - - let mut index_valid = IndexValid::All; + let mut index_valid = !0u32; for change in content_changes { - match change.range { - Some(range) => { - if !index_valid.covers(range.end.line) { - line_index.index = Arc::new(ide::LineIndex::new(old_text)); - } - index_valid = IndexValid::UpToLineExclusive(range.start.line); - if let Ok(range) = from_proto::text_range(&line_index, range) { - old_text.replace_range(Range::::from(range), &change.text); - } + // The None case can't happen as we have handled it above already + if let Some(range) = change.range { + if index_valid <= range.end.line { + *Arc::make_mut(&mut line_index.index) = ide::LineIndex::new(&text); } - None => { - *old_text = change.text; - index_valid = IndexValid::UpToLineExclusive(0); + index_valid = range.start.line; + if let Ok(range) = from_proto::text_range(&line_index, range) { + text.replace_range(Range::::from(range), &change.text); } } } + text } /// Checks that the edits inside the completion and the additional edits do not overlap. @@ -242,11 +250,10 @@ mod tests { }; } - let mut text = String::new(); - apply_document_changes(&mut text, vec![]); + let text = apply_document_changes(|| String::new(), vec![]); assert_eq!(text, ""); - apply_document_changes( - &mut text, + let text = apply_document_changes( + || text, vec![TextDocumentContentChangeEvent { range: None, range_length: None, @@ -254,39 +261,39 @@ mod tests { }], ); assert_eq!(text, "the"); - apply_document_changes(&mut text, c![0, 3; 0, 3 => " quick"]); + let text = apply_document_changes(|| text, c![0, 3; 0, 3 => " quick"]); assert_eq!(text, "the quick"); - apply_document_changes(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); + let text = apply_document_changes(|| text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); assert_eq!(text, "quick foxes"); - apply_document_changes(&mut text, c![0, 11; 0, 11 => "\ndream"]); + let text = apply_document_changes(|| text, c![0, 11; 0, 11 => "\ndream"]); assert_eq!(text, "quick foxes\ndream"); - apply_document_changes(&mut text, c![1, 0; 1, 0 => "have "]); + let text = apply_document_changes(|| text, c![1, 0; 1, 0 => "have "]); assert_eq!(text, "quick foxes\nhave dream"); - apply_document_changes( - &mut text, + let text = apply_document_changes( + || text, c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"], ); assert_eq!(text, "the quick foxes\nhave quiet dreams\n"); - apply_document_changes(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]); + let text = apply_document_changes(|| text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]); assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n"); - apply_document_changes( - &mut text, + let text = apply_document_changes( + || text, c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"], ); assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n"); - apply_document_changes(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); + let text = apply_document_changes(|| text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); assert_eq!(text, "the quick \nthey have quiet dreams\n"); - text = String::from("❤️"); - apply_document_changes(&mut text, c![0, 0; 0, 0 => "a"]); + let text = String::from("❤️"); + let text = apply_document_changes(|| text, c![0, 0; 0, 0 => "a"]); assert_eq!(text, "a❤️"); - text = String::from("a\nb"); - apply_document_changes(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); + let text = String::from("a\nb"); + let text = apply_document_changes(|| text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); assert_eq!(text, "adcb"); - text = String::from("a\nb"); - apply_document_changes(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); + let text = String::from("a\nb"); + let text = apply_document_changes(|| text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); assert_eq!(text, "ațc\ncb"); } 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 7d10dc5d15b62..274588ce0e076 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 @@ -451,7 +451,7 @@ impl GlobalState { ProjectWorkspaceProgress::Begin => (Progress::Begin, None), ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)), ProjectWorkspaceProgress::End(workspaces) => { - self.fetch_workspaces_queue.op_completed(workspaces); + self.fetch_workspaces_queue.op_completed(Some(workspaces)); let old = Arc::clone(&self.workspaces); self.switch_workspaces("fetched workspace".to_string()); @@ -759,8 +759,10 @@ impl GlobalState { let vfs = &mut this.vfs.write().0; let file_id = vfs.file_id(&path).unwrap(); - let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); - apply_document_changes(&mut text, params.content_changes); + let text = apply_document_changes( + || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(), + params.content_changes, + ); vfs.set_file_contents(path, Some(text.into_bytes())); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs index f86a0f66ad8d6..45a1dab9772fa 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs @@ -7,7 +7,7 @@ use vfs::VfsPath; /// Holds the set of in-memory documents. /// -/// For these document, there true contents is maintained by the client. It +/// For these document, their true contents is maintained by the client. It /// might be different from what's on disk. #[derive(Default, Clone)] pub(crate) struct MemDocs { @@ -19,6 +19,7 @@ impl MemDocs { pub(crate) fn contains(&self, path: &VfsPath) -> bool { self.mem_docs.contains_key(path) } + pub(crate) fn insert(&mut self, path: VfsPath, data: DocumentData) -> Result<(), ()> { self.added_or_removed = true; match self.mem_docs.insert(path, data) { @@ -26,6 +27,7 @@ impl MemDocs { None => Ok(()), } } + pub(crate) fn remove(&mut self, path: &VfsPath) -> Result<(), ()> { self.added_or_removed = true; match self.mem_docs.remove(path) { @@ -33,17 +35,21 @@ impl MemDocs { None => Err(()), } } + pub(crate) fn get(&self, path: &VfsPath) -> Option<&DocumentData> { self.mem_docs.get(path) } + pub(crate) fn get_mut(&mut self, path: &VfsPath) -> Option<&mut DocumentData> { // NB: don't set `self.added_or_removed` here, as that purposefully only // tracks changes to the key set. self.mem_docs.get_mut(path) } + pub(crate) fn iter(&self) -> impl Iterator { self.mem_docs.keys() } + pub(crate) fn take_changes(&mut self) -> bool { mem::replace(&mut self.added_or_removed, false) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index e1f651786dee4..fcfe4be0b8cec 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -106,6 +106,14 @@ impl GlobalState { status.health = lsp_ext::Health::Error; status.message = Some(error) } + + if self.config.linked_projects().is_empty() + && self.config.detached_files().is_empty() + && self.config.notifications().cargo_toml_not_found + { + status.health = lsp_ext::Health::Warning; + status.message = Some("Workspace reload required".to_string()) + } status } @@ -198,12 +206,9 @@ impl GlobalState { self.show_and_log_error("failed to run build scripts".to_string(), Some(error)); } - let workspaces = self - .fetch_workspaces_queue - .last_op_result() - .iter() - .filter_map(|res| res.as_ref().ok().cloned()) - .collect::>(); + let Some(workspaces) = self.fetch_workspaces_queue.last_op_result() else { return; }; + let workspaces = + workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::>(); fn eq_ignore_build_data<'a>( left: &'a ProjectWorkspace, @@ -300,9 +305,6 @@ impl GlobalState { let files_config = self.config.files(); let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude); - let standalone_server_name = - format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX); - if self.proc_macro_clients.is_empty() { if let Some((path, path_manually_set)) = self.config.proc_macro_srv() { tracing::info!("Spawning proc-macro servers"); @@ -310,40 +312,17 @@ impl GlobalState { .workspaces .iter() .map(|ws| { - let (path, args) = if path_manually_set { + let (path, args): (_, &[_]) = if path_manually_set { tracing::debug!( "Pro-macro server path explicitly set: {}", path.display() ); - (path.clone(), vec![]) + (path.clone(), &[]) } else { - let mut sysroot_server = None; - if let ProjectWorkspace::Cargo { sysroot, .. } - | ProjectWorkspace::Json { sysroot, .. } = ws - { - if let Some(sysroot) = sysroot.as_ref() { - let server_path = sysroot - .root() - .join("libexec") - .join(&standalone_server_name); - if std::fs::metadata(&server_path).is_ok() { - tracing::debug!( - "Sysroot proc-macro server exists at {}", - server_path.display() - ); - sysroot_server = Some(server_path); - } else { - tracing::debug!( - "Sysroot proc-macro server does not exist at {}", - server_path.display() - ); - } - } + match ws.find_sysroot_proc_macro_srv() { + Some(server_path) => (server_path, &[]), + None => (path.clone(), &["proc-macro"]), } - sysroot_server.map_or_else( - || (path.clone(), vec!["proc-macro".to_owned()]), - |path| (path, vec![]), - ) }; tracing::info!(?args, "Using proc-macro server at {}", path.display(),); @@ -427,9 +406,14 @@ impl GlobalState { fn fetch_workspace_error(&self) -> Result<(), String> { let mut buf = String::new(); - for ws in self.fetch_workspaces_queue.last_op_result() { - if let Err(err) = ws { - stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err); + let Some(last_op_result) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) }; + if last_op_result.is_empty() { + stdx::format_to!(buf, "rust-analyzer failed to discover workspace"); + } else { + for ws in last_op_result { + if let Err(err) = ws { + stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err); + } } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs index 6c84a2069cd57..81cc1952ba5ca 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs @@ -24,7 +24,7 @@ use crate::{ line_index::{LineEndings, LineIndex, PositionEncoding}, lsp_ext, lsp_utils::invalid_params_error, - semantic_tokens, Result, + semantic_tokens, }; pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { @@ -429,7 +429,7 @@ pub(crate) fn inlay_hint( line_index: &LineIndex, render_colons: bool, mut inlay_hint: InlayHint, -) -> Result { +) -> Cancellable { match inlay_hint.kind { InlayKind::ParameterHint if render_colons => inlay_hint.label.append_str(":"), InlayKind::TypeHint if render_colons => inlay_hint.label.prepend_str(": "), @@ -440,32 +440,35 @@ pub(crate) fn inlay_hint( Ok(lsp_types::InlayHint { position: match inlay_hint.kind { // before annotated thing - InlayKind::ParameterHint - | InlayKind::ImplicitReborrowHint - | InlayKind::BindingModeHint => position(line_index, inlay_hint.range.start()), + InlayKind::ParameterHint | InlayKind::AdjustmentHint | InlayKind::BindingModeHint => { + position(line_index, inlay_hint.range.start()) + } // after annotated thing InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint | InlayKind::GenericParamListHint + | InlayKind::AdjustmentHintClosingParenthesis | InlayKind::LifetimeHint | InlayKind::ClosingBraceHint => position(line_index, inlay_hint.range.end()), }, padding_left: Some(match inlay_hint.kind { InlayKind::TypeHint => !render_colons, InlayKind::ChainingHint | InlayKind::ClosingBraceHint => true, - InlayKind::BindingModeHint + InlayKind::AdjustmentHintClosingParenthesis + | InlayKind::BindingModeHint | InlayKind::ClosureReturnTypeHint | InlayKind::GenericParamListHint - | InlayKind::ImplicitReborrowHint + | InlayKind::AdjustmentHint | InlayKind::LifetimeHint | InlayKind::ParameterHint => false, }), padding_right: Some(match inlay_hint.kind { - InlayKind::ChainingHint + InlayKind::AdjustmentHintClosingParenthesis + | InlayKind::ChainingHint | InlayKind::ClosureReturnTypeHint | InlayKind::GenericParamListHint - | InlayKind::ImplicitReborrowHint + | InlayKind::AdjustmentHint | InlayKind::TypeHint | InlayKind::ClosingBraceHint => false, InlayKind::BindingModeHint => inlay_hint.label.as_simple_str() != Some("&"), @@ -476,10 +479,11 @@ pub(crate) fn inlay_hint( InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => { Some(lsp_types::InlayHintKind::TYPE) } - InlayKind::BindingModeHint + InlayKind::AdjustmentHintClosingParenthesis + | InlayKind::BindingModeHint | InlayKind::GenericParamListHint | InlayKind::LifetimeHint - | InlayKind::ImplicitReborrowHint + | InlayKind::AdjustmentHint | InlayKind::ClosingBraceHint => None, }, text_edits: None, @@ -518,7 +522,7 @@ pub(crate) fn inlay_hint( fn inlay_hint_label( snap: &GlobalStateSnapshot, label: InlayHintLabel, -) -> Result { +) -> Cancellable { Ok(match label.as_simple_str() { Some(s) => lsp_types::InlayHintLabel::String(s.into()), None => lsp_types::InlayHintLabel::LabelParts( @@ -536,7 +540,7 @@ fn inlay_hint_label( command: None, }) }) - .collect::>>()?, + .collect::>>()?, ), }) } @@ -794,7 +798,7 @@ pub(crate) fn optional_versioned_text_document_identifier( pub(crate) fn location( snap: &GlobalStateSnapshot, frange: FileRange, -) -> Result { +) -> Cancellable { let url = url(snap, frange.file_id); let line_index = snap.file_line_index(frange.file_id)?; let range = range(&line_index, frange.range); @@ -806,7 +810,7 @@ pub(crate) fn location( pub(crate) fn location_from_nav( snap: &GlobalStateSnapshot, nav: NavigationTarget, -) -> Result { +) -> Cancellable { let url = url(snap, nav.file_id); let line_index = snap.file_line_index(nav.file_id)?; let range = range(&line_index, nav.full_range); @@ -818,7 +822,7 @@ pub(crate) fn location_link( snap: &GlobalStateSnapshot, src: Option, target: NavigationTarget, -) -> Result { +) -> Cancellable { let origin_selection_range = match src { Some(src) => { let line_index = snap.file_line_index(src.file_id)?; @@ -840,7 +844,7 @@ pub(crate) fn location_link( fn location_info( snap: &GlobalStateSnapshot, target: NavigationTarget, -) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { +) -> Cancellable<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { let line_index = snap.file_line_index(target.file_id)?; let target_uri = url(snap, target.file_id); @@ -854,12 +858,12 @@ pub(crate) fn goto_definition_response( snap: &GlobalStateSnapshot, src: Option, targets: Vec, -) -> Result { +) -> Cancellable { if snap.config.location_link() { let links = targets .into_iter() .map(|nav| location_link(snap, src, nav)) - .collect::>>()?; + .collect::>>()?; Ok(links.into()) } else { let locations = targets @@ -867,7 +871,7 @@ pub(crate) fn goto_definition_response( .map(|nav| { location(snap, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }) }) - .collect::>>()?; + .collect::>>()?; Ok(locations.into()) } } @@ -881,7 +885,7 @@ pub(crate) fn snippet_text_document_edit( is_snippet: bool, file_id: FileId, edit: TextEdit, -) -> Result { +) -> Cancellable { let text_document = optional_versioned_text_document_identifier(snap, file_id); let line_index = snap.file_line_index(file_id)?; let mut edits: Vec<_> = @@ -958,7 +962,7 @@ pub(crate) fn snippet_text_document_ops( pub(crate) fn snippet_workspace_edit( snap: &GlobalStateSnapshot, source_change: SourceChange, -) -> Result { +) -> Cancellable { let mut document_changes: Vec = Vec::new(); for op in source_change.file_system_edits { @@ -995,7 +999,7 @@ pub(crate) fn snippet_workspace_edit( pub(crate) fn workspace_edit( snap: &GlobalStateSnapshot, source_change: SourceChange, -) -> Result { +) -> Cancellable { assert!(!source_change.is_snippet); snippet_workspace_edit(snap, source_change).map(|it| it.into()) } @@ -1048,7 +1052,7 @@ impl From pub(crate) fn call_hierarchy_item( snap: &GlobalStateSnapshot, target: NavigationTarget, -) -> Result { +) -> Cancellable { let name = target.name.to_string(); let detail = target.description.clone(); let kind = target.kind.map(symbol_kind).unwrap_or(lsp_types::SymbolKind::FUNCTION); @@ -1080,7 +1084,7 @@ pub(crate) fn code_action( snap: &GlobalStateSnapshot, assist: Assist, resolve_data: Option<(usize, lsp_types::CodeActionParams)>, -) -> Result { +) -> Cancellable { let mut res = lsp_ext::CodeAction { title: assist.label.to_string(), group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0), @@ -1113,13 +1117,13 @@ pub(crate) fn code_action( pub(crate) fn runnable( snap: &GlobalStateSnapshot, runnable: Runnable, -) -> Result { +) -> Cancellable { let config = snap.config.runnables(); let spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id)?; let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone()); let target = spec.as_ref().map(|s| s.target.clone()); let (cargo_args, executable_args) = - CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg)?; + CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg); let label = runnable.label(target); let location = location_link(snap, None, runnable.nav)?; @@ -1142,7 +1146,7 @@ pub(crate) fn code_lens( acc: &mut Vec, snap: &GlobalStateSnapshot, annotation: Annotation, -) -> Result<()> { +) -> Cancellable<()> { let client_commands_config = snap.config.client_commands(); match annotation.kind { AnnotationKind::Runnable(run) => { diff --git a/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml b/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml index e75867e2d81cf..593dc4e55b21b 100644 --- a/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml +++ b/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index e0657ab0f6d1c..f7b7d09640ff7 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false @@ -16,7 +16,7 @@ always-assert = { version = "0.1.2", features = ["log"] } # Think twice before adding anything here [target.'cfg(windows)'.dependencies] -miow = "0.4.0" +miow = "0.5.0" winapi = { version = "0.3.9", features = ["winerror"] } [features] diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 1ef903371cf89..00743cca55934 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -5,7 +5,7 @@ description = "Comment and whitespace preserving parser for the Rust language" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/syntax/fuzz/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/fuzz/Cargo.toml index ba2f515b0bc1b..f295c40065dbd 100644 --- a/src/tools/rust-analyzer/crates/syntax/fuzz/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/fuzz/Cargo.toml @@ -4,7 +4,7 @@ name = "syntax-fuzz" version = "0.0.1" publish = false edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [package.metadata] cargo-fuzz = true diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 5379732ac6c37..0a0cb0290d6cb 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -239,8 +239,11 @@ Static = Trait = Attr* Visibility? 'unsafe'? 'auto'? - 'trait' Name GenericParamList? (':' TypeBoundList?)? WhereClause? - AssocItemList + 'trait' Name GenericParamList? + ( + (':' TypeBoundList?)? WhereClause? AssocItemList + | '=' TypeBoundList? WhereClause? ';' + ) AssocItemList = '{' Attr* AssocItem* '}' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 6cfb98d92fcf2..2ea715f47fb23 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -407,6 +407,8 @@ impl Trait { pub fn auto_token(&self) -> Option { support::token(&self.syntax, T![auto]) } pub fn trait_token(&self) -> Option { support::token(&self.syntax, T![trait]) } pub fn assoc_item_list(&self) -> Option { support::child(&self.syntax) } + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index ba72e64425b23..8990f7a7d4e8e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -209,17 +209,19 @@ impl ast::String { let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; let mut buf = String::new(); - let mut text_iter = text.chars(); + let mut prev_end = 0; let mut has_error = false; unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match ( unescaped_char, buf.capacity() == 0, ) { (Ok(c), false) => buf.push(c), - (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), + (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { + prev_end = char_range.end + } (Ok(c), true) => { buf.reserve_exact(text.len()); - buf.push_str(&text[..char_range.start]); + buf.push_str(&text[..prev_end]); buf.push(c); } (Err(_), _) => has_error = true, @@ -252,17 +254,19 @@ impl ast::ByteString { let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; let mut buf: Vec = Vec::new(); - let mut text_iter = text.chars(); + let mut prev_end = 0; let mut has_error = false; unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match ( unescaped_char, buf.capacity() == 0, ) { (Ok(c), false) => buf.push(c as u8), - (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), + (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { + prev_end = char_range.end + } (Ok(c), true) => { buf.reserve_exact(text.len()); - buf.extend_from_slice(text[..char_range.start].as_bytes()); + buf.extend_from_slice(text[..prev_end].as_bytes()); buf.push(c as u8); } (Err(_), _) => has_error = true, @@ -445,6 +449,36 @@ mod tests { check_string_value(r"\foobar", None); check_string_value(r"\nfoobar", "\nfoobar"); check_string_value(r"C:\\Windows\\System32\\", "C:\\Windows\\System32\\"); + check_string_value(r"\x61bcde", "abcde"); + check_string_value( + r"a\ +bcde", "abcde", + ); + } + + fn check_byte_string_value<'a, const N: usize>( + lit: &str, + expected: impl Into>, + ) { + assert_eq!( + ast::ByteString { syntax: make::tokens::literal(&format!("b\"{}\"", lit)) } + .value() + .as_deref(), + expected.into().map(|value| &value[..]) + ); + } + + #[test] + fn test_byte_string_escape() { + check_byte_string_value(r"foobar", b"foobar"); + check_byte_string_value(r"\foobar", None::<&[u8; 0]>); + check_byte_string_value(r"\nfoobar", b"\nfoobar"); + check_byte_string_value(r"C:\\Windows\\System32\\", b"C:\\Windows\\System32\\"); + check_byte_string_value(r"\x61bcde", b"abcde"); + check_byte_string_value( + r"a\ +bcde", b"abcde", + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs index 70b54843dbaab..712ef5f63b651 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs @@ -86,7 +86,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> String { .traits .iter() .filter(|trait_name| { - // Loops have two expressions so this might collide, therefor manual impl it + // Loops have two expressions so this might collide, therefore manual impl it node.name != "ForExpr" && node.name != "WhileExpr" || trait_name.as_str() != "HasLoopBody" }) diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml index cceafe04e3777..1047373b1c75e 100644 --- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml +++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml index 7a90d64a98ba9..8df7e1af61163 100644 --- a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml index 3e0f31f19c507..a6a3ae742aeb3 100644 --- a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml +++ b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index 52dfb86080418..4f2103f3a97fc 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index df5dc24e2cd12..061f3c157a88c 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml index d7549a2841539..e55bf6f293c43 100644 --- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/docs/dev/architecture.md b/src/tools/rust-analyzer/docs/dev/architecture.md index e3a4fdfda90c2..a07cf036e0604 100644 --- a/src/tools/rust-analyzer/docs/dev/architecture.md +++ b/src/tools/rust-analyzer/docs/dev/architecture.md @@ -481,7 +481,7 @@ It is not cheap enough to enable in prod, and this is a bug which should be fixe rust-analyzer strives to be as configurable as possible while offering reasonable defaults where no configuration exists yet. The rule of thumb is to enable most features by default unless they are buggy or degrade performance too much. There will always be features that some people find more annoying than helpful, so giving the users the ability to tweak or disable these is a big part of offering a good user experience. -Enabling them by default is a matter of discoverability, as many users end up don't know about some features even though they are presented in the manual. +Enabling them by default is a matter of discoverability, as many users don't know about some features even though they are presented in the manual. Mind the code--architecture gap: at the moment, we are using fewer feature flags than we really should. ### Serialization @@ -492,8 +492,8 @@ If a type is serializable, then it is a part of some IPC boundary. You often don't control the other side of this boundary, so changing serializable types is hard. For this reason, the types in `ide`, `base_db` and below are not serializable by design. -If such types need to cross an IPC boundary, then the client of rust-analyzer needs to provide custom, client-specific serialization format. +If such types need to cross an IPC boundary, then the client of rust-analyzer needs to provide a custom, client-specific serialization format. This isolates backwards compatibility and migration concerns to a specific client. -For example, `rust-project.json` is it's own format -- it doesn't include `CrateGraph` as is. +For example, `rust-project.json` is its own format -- it doesn't include `CrateGraph` as is. Instead, it creates a `CrateGraph` by calling appropriate constructing functions. diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index 36794efe42726..57f950034cbb7 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -173,7 +173,7 @@ Whether to pass `--no-default-features` to Cargo. Defaults to -- Override the command rust-analyzer uses instead of `cargo check` for diagnostics on save. The command is required to output json and -should therefor include `--message-format=json` or a similar option. +should therefore include `--message-format=json` or a similar option. If you're changing this because you're using some tool wrapping Cargo, you might also want to change @@ -190,11 +190,15 @@ cargo check --workspace --message-format=json --all-targets ``` . -- -[[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`):: +[[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `[]`):: + -- -Check for a specific target. Defaults to -`#rust-analyzer.cargo.target#`. +Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. + +Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. +`["aarch64-apple-darwin", "x86_64-apple-darwin"]`. + +Aliased as `"checkOnSave.targets"`. -- [[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: + @@ -450,6 +454,11 @@ to always show them). -- Whether to show inlay type hints for return types of closures. -- +[[rust-analyzer.inlayHints.expressionAdjustmentHints.enable]]rust-analyzer.inlayHints.expressionAdjustmentHints.enable (default: `"never"`):: ++ +-- +Whether to show inlay hints for type adjustments. +-- [[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`):: + -- @@ -474,7 +483,8 @@ site. [[rust-analyzer.inlayHints.reborrowHints.enable]]rust-analyzer.inlayHints.reborrowHints.enable (default: `"never"`):: + -- -Whether to show inlay type hints for compiler inserted reborrows. +Whether to show inlay hints for compiler inserted reborrows. +This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. -- [[rust-analyzer.inlayHints.renderColons]]rust-analyzer.inlayHints.renderColons (default: `true`):: + diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc index 49500e390a502..1a4c70575b033 100644 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ b/src/tools/rust-analyzer/docs/user/manual.adoc @@ -367,7 +367,7 @@ if executable('rust-analyzer') endif ---- -There is no dedicated UI for the server configuration, so you would need to send any options as a value of the `initialization_options` field, as described in the <<_configuration,Configuration>> section. +There is no dedicated UI for the server configuration, so you would need to send any options as a value of the `initialization_options` field, as described in the <> section. Here is an example of how to enable the proc-macro support: [source,vim] diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 1a97a9c089375..c4d4e428ea07d 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -396,6 +396,11 @@ "default": true, "type": "boolean" }, + "rust-analyzer.diagnostics.previewRustcOutput": { + "markdownDescription": "Whether to show the main part of the rendered rustc output of a diagnostic message.", + "default": false, + "type": "boolean" + }, "$generated-start": {}, "rust-analyzer.assist.emitMustUse": { "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.", @@ -623,7 +628,7 @@ ] }, "rust-analyzer.checkOnSave.overrideCommand": { - "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefor include `--message-format=json` or a similar option.\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects, this command is invoked for\neach of them, with the working directory being the project root\n(i.e., the folder containing the `Cargo.toml`).\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.", + "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option.\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects, this command is invoked for\neach of them, with the working directory being the project root\n(i.e., the folder containing the `Cargo.toml`).\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.", "default": null, "type": [ "null", @@ -634,11 +639,18 @@ } }, "rust-analyzer.checkOnSave.target": { - "markdownDescription": "Check for a specific target. Defaults to\n`#rust-analyzer.cargo.target#`.", - "default": null, - "type": [ - "null", - "string" + "markdownDescription": "Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.\n\nCan be a single target, e.g. `\"x86_64-unknown-linux-gnu\"` or a list of targets, e.g.\n`[\"aarch64-apple-darwin\", \"x86_64-apple-darwin\"]`.\n\nAliased as `\"checkOnSave.targets\"`.", + "default": [], + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } ] }, "rust-analyzer.completion.autoimport.enable": { @@ -935,6 +947,21 @@ "Only show type hints for return types of closures with blocks." ] }, + "rust-analyzer.inlayHints.expressionAdjustmentHints.enable": { + "markdownDescription": "Whether to show inlay hints for type adjustments.", + "default": "never", + "type": "string", + "enum": [ + "always", + "never", + "reborrow" + ], + "enumDescriptions": [ + "Always show all adjustment hints.", + "Never show adjustment hints.", + "Only show auto borrow and dereference adjustment hints." + ] + }, "rust-analyzer.inlayHints.lifetimeElisionHints.enable": { "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.", "default": "never", @@ -970,7 +997,7 @@ "type": "boolean" }, "rust-analyzer.inlayHints.reborrowHints.enable": { - "markdownDescription": "Whether to show inlay type hints for compiler inserted reborrows.", + "markdownDescription": "Whether to show inlay hints for compiler inserted reborrows.\nThis setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.", "default": "never", "type": "string", "enum": [ @@ -1276,6 +1303,11 @@ "$generated-end": {} } }, + "configurationDefaults": { + "explorer.fileNesting.patterns": { + "Cargo.toml": "Cargo.lock" + } + }, "problemPatterns": [ { "name": "rustc", diff --git a/src/tools/rust-analyzer/editors/code/src/client.ts b/src/tools/rust-analyzer/editors/code/src/client.ts index fb667619c86be..23e039722ee33 100644 --- a/src/tools/rust-analyzer/editors/code/src/client.ts +++ b/src/tools/rust-analyzer/editors/code/src/client.ts @@ -4,7 +4,7 @@ import * as ra from "../src/lsp_ext"; import * as Is from "vscode-languageclient/lib/common/utils/is"; import { assert } from "./util"; import { WorkspaceEdit } from "vscode"; -import { substituteVSCodeVariables } from "./config"; +import { Config, substituteVSCodeVariables } from "./config"; import { randomUUID } from "crypto"; export interface Env { @@ -66,7 +66,8 @@ export async function createClient( traceOutputChannel: vscode.OutputChannel, outputChannel: vscode.OutputChannel, initializationOptions: vscode.WorkspaceConfiguration, - serverOptions: lc.ServerOptions + serverOptions: lc.ServerOptions, + config: Config ): Promise { const clientOptions: lc.LanguageClientOptions = { documentSelector: [{ scheme: "file", language: "rust" }], @@ -99,6 +100,43 @@ export async function createClient( } }, }, + async handleDiagnostics( + uri: vscode.Uri, + diagnostics: vscode.Diagnostic[], + next: lc.HandleDiagnosticsSignature + ) { + const preview = config.previewRustcOutput; + diagnostics.forEach((diag, idx) => { + // Abuse the fact that VSCode leaks the LSP diagnostics data field through the + // Diagnostic class, if they ever break this we are out of luck and have to go + // back to the worst diagnostics experience ever:) + + // We encode the rendered output of a rustc diagnostic in the rendered field of + // the data payload of the lsp diagnostic. If that field exists, overwrite the + // diagnostic code such that clicking it opens the diagnostic in a readonly + // text editor for easy inspection + const rendered = (diag as unknown as { data?: { rendered?: string } }).data + ?.rendered; + if (rendered) { + if (preview) { + const index = rendered.match(/^(note|help):/m)?.index || 0; + diag.message = rendered + .substring(0, index) + .replace(/^ -->[^\n]+\n/m, ""); + } + diag.code = { + target: vscode.Uri.from({ + scheme: "rust-analyzer-diagnostics-view", + path: "/diagnostic message", + fragment: uri.toString(), + query: idx.toString(), + }), + value: "Click for full compiler diagnostic", + }; + } + }); + return next(uri, diagnostics); + }, async provideHover( document: vscode.TextDocument, position: vscode.Position, diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index 632a7d86faa36..d8dbd1df16dfb 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -238,6 +238,9 @@ export class Config { gotoTypeDef: this.get("hover.actions.gotoTypeDef.enable"), }; } + get previewRustcOutput() { + return this.get("diagnostics.previewRustcOutput"); + } } const VarRegex = new RegExp(/\$\{(.+?)\}/g); diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 3e366525ee295..d6cee5c8fc610 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -179,7 +179,8 @@ export class Ctx { this.traceOutputChannel, this.outputChannel, initializationOptions, - serverOptions + serverOptions, + this.config ); this.pushClientCleanup( this._client.onNotification(ra.serverStatus, (params) => diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index e76b657c1bfb5..25f1e83d109cb 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -48,6 +48,30 @@ async function activateServer(ctx: Ctx): Promise { ctx.pushExtCleanup(activateTaskProvider(ctx.config)); } + ctx.pushExtCleanup( + vscode.workspace.registerTextDocumentContentProvider( + "rust-analyzer-diagnostics-view", + new (class implements vscode.TextDocumentContentProvider { + async provideTextDocumentContent(uri: vscode.Uri): Promise { + const diags = ctx.client?.diagnostics?.get( + vscode.Uri.parse(uri.fragment, true) + ); + if (!diags) { + return "Unable to find original rustc diagnostic"; + } + + const diag = diags[parseInt(uri.query)]; + if (!diag) { + return "Unable to find original rustc diagnostic"; + } + const rendered = (diag as unknown as { data?: { rendered?: string } }).data + ?.rendered; + return rendered ?? "Unable to find original rustc diagnostic"; + } + })() + ) + ); + vscode.workspace.onDidChangeWorkspaceFolders( async (_) => ctx.onWorkspaceFolderChanges(), null, diff --git a/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml index 0be0bf920de9d..95e27beab5dc1 100644 --- a/src/tools/rust-analyzer/xtask/Cargo.toml +++ b/src/tools/rust-analyzer/xtask/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" publish = false license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [dependencies] anyhow = "1.0.62"