Skip to content

Commit c98fc53

Browse files
committed
Generalize reborrow hints as adjustment hints
1 parent cd26032 commit c98fc53

File tree

9 files changed

+182
-78
lines changed

9 files changed

+182
-78
lines changed

crates/hir-ty/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub use builder::{ParamKind, TyBuilder};
5353
pub use chalk_ext::*;
5454
pub use infer::{
5555
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
56-
InferenceResult,
56+
InferenceResult, OverloadedDeref, PointerCast,
5757
};
5858
pub use interner::Interner;
5959
pub use lower::{

crates/hir/src/lib.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ pub use {
117117
name::{known, Name},
118118
ExpandResult, HirFileId, InFile, MacroFile, Origin,
119119
},
120-
hir_ty::display::HirDisplay,
120+
hir_ty::{display::HirDisplay, PointerCast, Safety},
121121
};
122122

123123
// These are negative re-exports: pub using these names is forbidden, they
@@ -3651,6 +3651,28 @@ impl From<ItemInNs> for ScopeDef {
36513651
}
36523652
}
36533653

3654+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3655+
pub enum Adjust {
3656+
/// Go from ! to any type.
3657+
NeverToAny,
3658+
/// Dereference once, producing a place.
3659+
Deref(Option<OverloadedDeref>),
3660+
/// Take the address and produce either a `&` or `*` pointer.
3661+
Borrow(AutoBorrow),
3662+
Pointer(PointerCast),
3663+
}
3664+
3665+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3666+
pub enum AutoBorrow {
3667+
/// Converts from T to &T.
3668+
Ref(Mutability),
3669+
/// Converts from T to *T.
3670+
RawPtr(Mutability),
3671+
}
3672+
3673+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3674+
pub struct OverloadedDeref(pub Mutability);
3675+
36543676
pub trait HasVisibility {
36553677
fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
36563678
fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {

crates/hir/src/semantics.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ use crate::{
2929
db::HirDatabase,
3030
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
3131
source_analyzer::{resolve_hir_path, SourceAnalyzer},
32-
Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function,
33-
HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
34-
Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
32+
Access, Adjust, AutoBorrow, BindingMode, BuiltinAttr, Callable, ConstParam, Crate,
33+
DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local,
34+
Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, ToolModule, Trait, Type,
35+
TypeAlias, TypeParam, VariantDef,
3536
};
3637

3738
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -333,9 +334,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
333334
self.imp.resolve_trait(trait_)
334335
}
335336

336-
// FIXME: Figure out a nice interface to inspect adjustments
337-
pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
338-
self.imp.is_implicit_reborrow(expr)
337+
pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjust>> {
338+
self.imp.expr_adjustments(expr)
339339
}
340340

341341
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
@@ -1067,8 +1067,29 @@ impl<'db> SemanticsImpl<'db> {
10671067
}
10681068
}
10691069

1070-
fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
1071-
self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr)
1070+
fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjust>> {
1071+
let mutability = |m| match m {
1072+
hir_ty::Mutability::Not => Mutability::Shared,
1073+
hir_ty::Mutability::Mut => Mutability::Mut,
1074+
};
1075+
self.analyze(expr.syntax())?.expr_adjustments(self.db, expr).map(|it| {
1076+
it.iter()
1077+
.map(|adjust| match adjust.kind {
1078+
hir_ty::Adjust::NeverToAny => Adjust::NeverToAny,
1079+
hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => {
1080+
Adjust::Deref(Some(OverloadedDeref(mutability(m))))
1081+
}
1082+
hir_ty::Adjust::Deref(None) => Adjust::Deref(None),
1083+
hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
1084+
Adjust::Borrow(AutoBorrow::RawPtr(mutability(m)))
1085+
}
1086+
hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::Ref(m)) => {
1087+
Adjust::Borrow(AutoBorrow::Ref(mutability(m)))
1088+
}
1089+
hir_ty::Adjust::Pointer(pc) => Adjust::Pointer(pc),
1090+
})
1091+
.collect()
1092+
})
10721093
}
10731094

10741095
fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {

crates/hir/src/source_analyzer.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ use hir_ty::{
3838
UnsafeExpr,
3939
},
4040
method_resolution::{self, lang_names_for_bin_op},
41-
Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
42-
TyLoweringContext,
41+
Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext,
4342
};
4443
use itertools::Itertools;
4544
use smallvec::SmallVec;
@@ -156,21 +155,14 @@ impl SourceAnalyzer {
156155
Some(res)
157156
}
158157

159-
pub(crate) fn is_implicit_reborrow(
158+
pub(crate) fn expr_adjustments(
160159
&self,
161160
db: &dyn HirDatabase,
162161
expr: &ast::Expr,
163-
) -> Option<Mutability> {
162+
) -> Option<&[Adjustment]> {
164163
let expr_id = self.expr_id(db, expr)?;
165164
let infer = self.infer.as_ref()?;
166-
let adjustments = infer.expr_adjustments.get(&expr_id)?;
167-
adjustments.windows(2).find_map(|slice| match slice {
168-
&[Adjustment {kind: Adjust::Deref(None), ..}, Adjustment {kind: Adjust::Borrow(AutoBorrow::Ref(m)), ..}] => Some(match m {
169-
hir_ty::Mutability::Mut => Mutability::Mut,
170-
hir_ty::Mutability::Not => Mutability::Shared,
171-
}),
172-
_ => None,
173-
})
165+
infer.expr_adjustments.get(&expr_id).map(|v| &**v)
174166
}
175167

176168
pub(crate) fn type_of_expr(

crates/ide/src/inlay_hints.rs

Lines changed: 104 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use std::fmt;
22

33
use either::Either;
4-
use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo};
4+
use hir::{
5+
known, Adjust, AutoBorrow, Callable, HasVisibility, HirDisplay, Mutability, OverloadedDeref,
6+
PointerCast, Safety, Semantics, TypeInfo,
7+
};
58
use ide_db::{
69
base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap,
710
RootDatabase,
@@ -22,7 +25,7 @@ pub struct InlayHintsConfig {
2225
pub type_hints: bool,
2326
pub parameter_hints: bool,
2427
pub chaining_hints: bool,
25-
pub reborrow_hints: ReborrowHints,
28+
pub adjustment_hints: AdjustmentHints,
2629
pub closure_return_type_hints: ClosureReturnTypeHints,
2730
pub binding_mode_hints: bool,
2831
pub lifetime_elision_hints: LifetimeElisionHints,
@@ -48,7 +51,7 @@ pub enum LifetimeElisionHints {
4851
}
4952

5053
#[derive(Clone, Debug, PartialEq, Eq)]
51-
pub enum ReborrowHints {
54+
pub enum AdjustmentHints {
5255
Always,
5356
MutableOnly,
5457
Never,
@@ -61,7 +64,8 @@ pub enum InlayKind {
6164
ClosingBraceHint,
6265
ClosureReturnTypeHint,
6366
GenericParamListHint,
64-
ImplicitReborrowHint,
67+
AdjustmentHint,
68+
AdjustmentHintClosingParenthesis,
6569
LifetimeHint,
6670
ParameterHint,
6771
TypeHint,
@@ -115,6 +119,12 @@ impl From<String> for InlayHintLabel {
115119
}
116120
}
117121

122+
impl From<&str> for InlayHintLabel {
123+
fn from(s: &str) -> Self {
124+
Self { parts: vec![InlayHintLabelPart { text: s.into(), linked_location: None }] }
125+
}
126+
}
127+
118128
impl fmt::Display for InlayHintLabel {
119129
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120130
write!(f, "{}", self.parts.iter().map(|part| &part.text).format(""))
@@ -221,6 +231,7 @@ fn hints(
221231
match node {
222232
ast::Expr(expr) => {
223233
chaining_hints(hints, sema, &famous_defs, config, file_id, &expr);
234+
adjustment_hints(hints, sema, config, &expr);
224235
match expr {
225236
ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)),
226237
ast::Expr::MethodCallExpr(it) => {
@@ -229,7 +240,7 @@ fn hints(
229240
ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, file_id, it),
230241
// We could show reborrows for all expressions, but usually that is just noise to the user
231242
// and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
232-
ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
243+
// ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
233244
_ => None,
234245
}
235246
},
@@ -617,30 +628,83 @@ fn closure_ret_hints(
617628
Some(())
618629
}
619630

620-
fn reborrow_hints(
631+
fn adjustment_hints(
621632
acc: &mut Vec<InlayHint>,
622633
sema: &Semantics<'_, RootDatabase>,
623634
config: &InlayHintsConfig,
624635
expr: &ast::Expr,
625636
) -> Option<()> {
626-
if config.reborrow_hints == ReborrowHints::Never {
627-
return None;
637+
if config.adjustment_hints == AdjustmentHints::Never {
638+
// return None;
628639
}
629640

641+
let parent = expr.syntax().parent().and_then(ast::Expr::cast);
630642
let descended = sema.descend_node_into_attributes(expr.clone()).pop();
631643
let desc_expr = descended.as_ref().unwrap_or(expr);
632-
let mutability = sema.is_implicit_reborrow(desc_expr)?;
633-
let label = match mutability {
634-
hir::Mutability::Shared if config.reborrow_hints != ReborrowHints::MutableOnly => "&*",
635-
hir::Mutability::Mut => "&mut *",
636-
_ => return None,
644+
let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
645+
let needs_parens = match parent {
646+
Some(parent) => {
647+
match parent {
648+
ast::Expr::AwaitExpr(_)
649+
| ast::Expr::CallExpr(_)
650+
| ast::Expr::CastExpr(_)
651+
| ast::Expr::FieldExpr(_)
652+
| ast::Expr::MethodCallExpr(_)
653+
| ast::Expr::TryExpr(_) => true,
654+
// FIXME: shorthands need special casing, though not sure if adjustments are even valid there
655+
ast::Expr::RecordExpr(_) => false,
656+
ast::Expr::IndexExpr(index) => index.base().as_ref() == Some(expr),
657+
_ => false,
658+
}
659+
}
660+
None => false,
637661
};
638-
acc.push(InlayHint {
639-
range: expr.syntax().text_range(),
640-
kind: InlayKind::ImplicitReborrowHint,
641-
label: label.to_string().into(),
642-
tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())),
643-
});
662+
if needs_parens {
663+
acc.push(InlayHint {
664+
range: expr.syntax().text_range(),
665+
kind: InlayKind::AdjustmentHint,
666+
label: "(".into(),
667+
tooltip: None,
668+
});
669+
}
670+
for adjustment in adjustments.into_iter().rev() {
671+
// FIXME: Add some nicer tooltips to each of these
672+
let text = match adjustment {
673+
Adjust::NeverToAny => "<never-to-any>",
674+
Adjust::Deref(None) => "*",
675+
Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => "*",
676+
Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => "*",
677+
Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => "&",
678+
Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => "&mut ",
679+
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => "&raw const ",
680+
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => "&raw mut ",
681+
// some of these could be represented via `as` casts, but that's not too nice and
682+
// handling everything as a prefix expr makes the `(` and `)` insertion easier
683+
Adjust::Pointer(cast) => match cast {
684+
PointerCast::ReifyFnPointer => "<fn-item-to-fn-pointer>",
685+
PointerCast::UnsafeFnPointer => "<safe-fn-pointer-to-unsafe-fn-pointer>",
686+
PointerCast::ClosureFnPointer(Safety::Unsafe) => "<closure-to-unsafe-fn-pointer>",
687+
PointerCast::ClosureFnPointer(Safety::Safe) => "<closure-to-fn-pointer>",
688+
PointerCast::MutToConstPointer => "<mut-ptr-to-const-ptr>",
689+
PointerCast::ArrayToPointer => "<array-ptr-to-element-ptr>",
690+
PointerCast::Unsize => "<unsize>",
691+
},
692+
};
693+
acc.push(InlayHint {
694+
range: expr.syntax().text_range(),
695+
kind: InlayKind::AdjustmentHint,
696+
label: text.into(),
697+
tooltip: None,
698+
});
699+
}
700+
if needs_parens {
701+
acc.push(InlayHint {
702+
range: expr.syntax().text_range(),
703+
kind: InlayKind::AdjustmentHintClosingParenthesis,
704+
label: ")".into(),
705+
tooltip: None,
706+
});
707+
}
644708
Some(())
645709
}
646710

@@ -785,23 +849,23 @@ fn binding_mode_hints(
785849
tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
786850
});
787851
});
788-
match pat {
789-
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
790-
let bm = sema.binding_mode_of_pat(pat)?;
791-
let bm = match bm {
792-
hir::BindingMode::Move => return None,
793-
hir::BindingMode::Ref(Mutability::Mut) => "ref mut",
794-
hir::BindingMode::Ref(Mutability::Shared) => "ref",
795-
};
796-
acc.push(InlayHint {
797-
range,
798-
kind: InlayKind::BindingModeHint,
799-
label: bm.to_string().into(),
800-
tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
801-
});
802-
}
803-
_ => (),
804-
}
852+
// match pat {
853+
// ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
854+
// let bm = sema.binding_mode_of_pat(pat)?;
855+
// let bm = match bm {
856+
// hir::BindingMode::Move => return None,
857+
// hir::BindingMode::Ref(Mutability::Mut) => "ref mut",
858+
// hir::BindingMode::Ref(Mutability::Shared) => "ref",
859+
// };
860+
// acc.push(InlayHint {
861+
// range,
862+
// kind: InlayKind::BindingModeHint,
863+
// label: bm.to_string().into(),
864+
// tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
865+
// });
866+
// }
867+
// _ => (),
868+
// }
805869

806870
Some(())
807871
}
@@ -1218,7 +1282,7 @@ mod tests {
12181282
use syntax::{TextRange, TextSize};
12191283
use test_utils::extract_annotations;
12201284

1221-
use crate::inlay_hints::ReborrowHints;
1285+
use crate::inlay_hints::AdjustmentHints;
12221286
use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
12231287

12241288
use super::ClosureReturnTypeHints;
@@ -1230,7 +1294,7 @@ mod tests {
12301294
chaining_hints: false,
12311295
lifetime_elision_hints: LifetimeElisionHints::Never,
12321296
closure_return_type_hints: ClosureReturnTypeHints::Never,
1233-
reborrow_hints: ReborrowHints::Always,
1297+
adjustment_hints: AdjustmentHints::Always,
12341298
binding_mode_hints: false,
12351299
hide_named_constructor_hints: false,
12361300
hide_closure_initialization_hints: false,
@@ -1242,7 +1306,7 @@ mod tests {
12421306
type_hints: true,
12431307
parameter_hints: true,
12441308
chaining_hints: true,
1245-
reborrow_hints: ReborrowHints::Always,
1309+
adjustment_hints: AdjustmentHints::Always,
12461310
closure_return_type_hints: ClosureReturnTypeHints::WithBlock,
12471311
binding_mode_hints: true,
12481312
lifetime_elision_hints: LifetimeElisionHints::Always,
@@ -2849,7 +2913,7 @@ impl () {
28492913
fn hints_implicit_reborrow() {
28502914
check_with_config(
28512915
InlayHintsConfig {
2852-
reborrow_hints: ReborrowHints::Always,
2916+
adjustment_hints: AdjustmentHints::Always,
28532917
parameter_hints: true,
28542918
..DISABLED_CONFIG
28552919
},

crates/ide/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ pub use crate::{
8181
highlight_related::{HighlightRelatedConfig, HighlightedRange},
8282
hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
8383
inlay_hints::{
84-
ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
85-
InlayTooltip, LifetimeElisionHints, ReborrowHints,
84+
AdjustmentHints, ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig,
85+
InlayKind, InlayTooltip, LifetimeElisionHints,
8686
},
8787
join_lines::JoinLinesConfig,
8888
markup::Markup,

0 commit comments

Comments
 (0)