From c98fc537e6d2c0d3c9a19582962a3db8bf2c1827 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 4 Nov 2022 17:11:15 +0100 Subject: [PATCH 1/4] Generalize reborrow hints as adjustment hints --- crates/hir-ty/src/lib.rs | 2 +- crates/hir/src/lib.rs | 24 ++++- crates/hir/src/semantics.rs | 37 +++++-- crates/hir/src/source_analyzer.rs | 16 +-- crates/ide/src/inlay_hints.rs | 144 +++++++++++++++++++-------- crates/ide/src/lib.rs | 4 +- crates/ide/src/static_index.rs | 2 +- crates/rust-analyzer/src/config.rs | 9 +- crates/rust-analyzer/src/to_proto.rs | 22 ++-- 9 files changed, 182 insertions(+), 78 deletions(-) diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index b68c764bdc..ad33053ad0 100644 --- a/crates/hir-ty/src/lib.rs +++ b/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::{ diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index cbd9bf32a5..9d77f343bc 100644 --- a/crates/hir/src/lib.rs +++ b/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 @@ -3651,6 +3651,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/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 119ec3210e..2e1f88ba09 100644 --- a/crates/hir/src/semantics.rs +++ b/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/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index f86c571005..91ea1c24d1 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/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/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 34d8bf67a3..a26ff1893c 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/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,7 +51,7 @@ pub enum LifetimeElisionHints { } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum ReborrowHints { +pub enum AdjustmentHints { Always, MutableOnly, 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("")) @@ -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,83 @@ 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 { - return None; + if config.adjustment_hints == AdjustmentHints::Never { + // 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 => "", + 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) => match cast { + PointerCast::ReifyFnPointer => "", + PointerCast::UnsafeFnPointer => "", + PointerCast::ClosureFnPointer(Safety::Unsafe) => "", + PointerCast::ClosureFnPointer(Safety::Safe) => "", + PointerCast::MutToConstPointer => "", + PointerCast::ArrayToPointer => "", + PointerCast::Unsize => "", + }, + }; + 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(()) } @@ -785,23 +849,23 @@ fn binding_mode_hints( tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), }); }); - match pat { - ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => { - let bm = sema.binding_mode_of_pat(pat)?; - let bm = match bm { - hir::BindingMode::Move => return None, - hir::BindingMode::Ref(Mutability::Mut) => "ref mut", - hir::BindingMode::Ref(Mutability::Shared) => "ref", - }; - acc.push(InlayHint { - range, - kind: InlayKind::BindingModeHint, - label: bm.to_string().into(), - tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), - }); - } - _ => (), - } + // match pat { + // ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => { + // let bm = sema.binding_mode_of_pat(pat)?; + // let bm = match bm { + // hir::BindingMode::Move => return None, + // hir::BindingMode::Ref(Mutability::Mut) => "ref mut", + // hir::BindingMode::Ref(Mutability::Shared) => "ref", + // }; + // acc.push(InlayHint { + // range, + // kind: InlayKind::BindingModeHint, + // label: bm.to_string().into(), + // tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), + // }); + // } + // _ => (), + // } Some(()) } @@ -1218,7 +1282,7 @@ mod tests { 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 +1294,7 @@ mod tests { chaining_hints: false, lifetime_elision_hints: LifetimeElisionHints::Never, closure_return_type_hints: ClosureReturnTypeHints::Never, - reborrow_hints: ReborrowHints::Always, + adjustment_hints: AdjustmentHints::Always, binding_mode_hints: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, @@ -1242,7 +1306,7 @@ mod tests { type_hints: true, parameter_hints: true, chaining_hints: true, - reborrow_hints: ReborrowHints::Always, + adjustment_hints: AdjustmentHints::Always, closure_return_type_hints: ClosureReturnTypeHints::WithBlock, binding_mode_hints: true, lifetime_elision_hints: LifetimeElisionHints::Always, @@ -2849,7 +2913,7 @@ impl () { fn hints_implicit_reborrow() { check_with_config( InlayHintsConfig { - reborrow_hints: ReborrowHints::Always, + adjustment_hints: AdjustmentHints::Always, parameter_hints: true, ..DISABLED_CONFIG }, diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 416817ca0b..568c53f8bd 100644 --- a/crates/ide/src/lib.rs +++ b/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, diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 27ad1a948d..6ebd6f713f 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -111,7 +111,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/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 4072ae585d..12c59edf9a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -261,6 +261,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 (`->`). @@ -1200,10 +1201,10 @@ 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_reborrowHints_enable { + ReborrowHintsDef::Always => ide::AdjustmentHints::Always, + ReborrowHintsDef::Never => ide::AdjustmentHints::Never, + ReborrowHintsDef::Mutable => ide::AdjustmentHints::MutableOnly, }, binding_mode_hints: self.data.inlayHints_bindingModeHints_enable, param_names_for_lifetime_elision_hints: self diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 6c84a2069c..6f44a1de63 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -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, From 95d20fccd77869a98ed8db1e8b0d45d4d909f26b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 4 Nov 2022 22:40:06 +0100 Subject: [PATCH 2/4] Add adjustment hint tests --- crates/ide/src/inlay_hints.rs | 121 ++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 43 deletions(-) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index a26ff1893c..9eef5aaeed 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -635,7 +635,12 @@ fn adjustment_hints( expr: &ast::Expr, ) -> Option<()> { if config.adjustment_hints == AdjustmentHints::Never { - // return None; + 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); @@ -2909,48 +2914,6 @@ impl () { ); } - #[test] - fn hints_implicit_reborrow() { - check_with_config( - InlayHintsConfig { - adjustment_hints: AdjustmentHints::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( @@ -3058,4 +3021,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) {} +} +"#, + ) + } } From d841ad116a3d438c4b04db1d895d9cc4991ca2c0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 4 Nov 2022 22:59:07 +0100 Subject: [PATCH 3/4] Fix up adjustment hints configurations --- crates/ide/src/inlay_hints.rs | 29 ++++++++++++-------- crates/rust-analyzer/src/config.rs | 43 ++++++++++++++++++++++++++---- docs/user/generated_config.adoc | 8 +++++- editors/code/package.json | 17 +++++++++++- 4 files changed, 79 insertions(+), 18 deletions(-) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 9eef5aaeed..50934a27f8 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -53,7 +53,7 @@ pub enum LifetimeElisionHints { #[derive(Clone, Debug, PartialEq, Eq)] pub enum AdjustmentHints { Always, - MutableOnly, + ReborrowOnly, Never, } @@ -675,7 +675,9 @@ fn adjustment_hints( for adjustment in adjustments.into_iter().rev() { // FIXME: Add some nicer tooltips to each of these let text = match adjustment { - Adjust::NeverToAny => "", + Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => { + "" + } Adjust::Deref(None) => "*", Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => "*", Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => "*", @@ -685,15 +687,20 @@ fn adjustment_hints( 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) => match cast { - PointerCast::ReifyFnPointer => "", - PointerCast::UnsafeFnPointer => "", - PointerCast::ClosureFnPointer(Safety::Unsafe) => "", - PointerCast::ClosureFnPointer(Safety::Safe) => "", - PointerCast::MutToConstPointer => "", - PointerCast::ArrayToPointer => "", - PointerCast::Unsize => "", - }, + 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(), diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 12c59edf9a..766937d444 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -321,6 +321,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. @@ -330,7 +332,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", @@ -1201,10 +1204,15 @@ impl Config { hide_closure_initialization_hints: self .data .inlayHints_typeHints_hideClosureInitialization, - adjustment_hints: match self.data.inlayHints_reborrowHints_enable { - ReborrowHintsDef::Always => ide::AdjustmentHints::Always, - ReborrowHintsDef::Never => ide::AdjustmentHints::Never, - ReborrowHintsDef::Mutable => ide::AdjustmentHints::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 @@ -1539,6 +1547,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); } @@ -1688,6 +1697,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 { @@ -1997,6 +2017,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": [ { diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 36794efe42..4f40ab25a1 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -450,6 +450,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 +479,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/editors/code/package.json b/editors/code/package.json index 8e8b59159d..f14123469b 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -935,6 +935,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 +985,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": [ From ee2dd934caeeb2fb044ac1cacd3e65c3b033bc8b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 7 Nov 2022 12:49:52 +0100 Subject: [PATCH 4/4] Don't trigger adjustment hints in all inlay hint tests --- crates/ide/src/inlay_hints.rs | 37 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 50934a27f8..325e609054 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -861,23 +861,23 @@ fn binding_mode_hints( tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), }); }); - // match pat { - // ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => { - // let bm = sema.binding_mode_of_pat(pat)?; - // let bm = match bm { - // hir::BindingMode::Move => return None, - // hir::BindingMode::Ref(Mutability::Mut) => "ref mut", - // hir::BindingMode::Ref(Mutability::Shared) => "ref", - // }; - // acc.push(InlayHint { - // range, - // kind: InlayKind::BindingModeHint, - // label: bm.to_string().into(), - // tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), - // }); - // } - // _ => (), - // } + match pat { + ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => { + let bm = sema.binding_mode_of_pat(pat)?; + let bm = match bm { + hir::BindingMode::Move => return None, + hir::BindingMode::Ref(Mutability::Mut) => "ref mut", + hir::BindingMode::Ref(Mutability::Shared) => "ref", + }; + acc.push(InlayHint { + range, + kind: InlayKind::BindingModeHint, + label: bm.to_string().into(), + tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), + }); + } + _ => (), + } Some(()) } @@ -1306,7 +1306,7 @@ mod tests { chaining_hints: false, lifetime_elision_hints: LifetimeElisionHints::Never, closure_return_type_hints: ClosureReturnTypeHints::Never, - adjustment_hints: AdjustmentHints::Always, + adjustment_hints: AdjustmentHints::Never, binding_mode_hints: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, @@ -1318,7 +1318,6 @@ mod tests { type_hints: true, parameter_hints: true, chaining_hints: true, - adjustment_hints: AdjustmentHints::Always, closure_return_type_hints: ClosureReturnTypeHints::WithBlock, binding_mode_hints: true, lifetime_elision_hints: LifetimeElisionHints::Always,