Skip to content

Commit 25ff717

Browse files
committed
Introduce TypeInfo
1 parent 29506b5 commit 25ff717

32 files changed

+127
-124
lines changed

crates/hir/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ pub use crate::{
8989
UnresolvedModule, UnresolvedProcMacro,
9090
},
9191
has_source::HasSource,
92-
semantics::{PathResolution, Semantics, SemanticsScope},
92+
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
9393
};
9494

9595
// Be careful with these re-exports.

crates/hir/src/semantics.rs

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,28 @@ impl PathResolution {
8787
}
8888
}
8989

90+
#[derive(Debug)]
91+
pub struct TypeInfo {
92+
/// The original type of the expression or pattern.
93+
pub ty: Type,
94+
/// The coerced type, if a coercion happened.
95+
pub coerced: Option<Type>,
96+
}
97+
98+
impl TypeInfo {
99+
pub fn ty(self) -> Type {
100+
self.ty
101+
}
102+
103+
pub fn coerced(self) -> Option<Type> {
104+
self.coerced
105+
}
106+
107+
pub fn coerced_or_original(self) -> Type {
108+
self.coerced.unwrap_or(self.ty)
109+
}
110+
}
111+
90112
/// Primary API to get semantic information, like types, from syntax trees.
91113
pub struct Semantics<'db, DB> {
92114
pub db: &'db DB,
@@ -212,23 +234,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
212234
self.imp.resolve_type(ty)
213235
}
214236

215-
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
237+
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
216238
self.imp.type_of_expr(expr)
217239
}
218240

219-
/// Returns true in case a coercion happened.
220-
pub fn type_of_expr_with_coercion(&self, expr: &ast::Expr) -> Option<(Type, bool)> {
221-
self.imp.type_of_expr_with_coercion(expr)
222-
}
223-
224-
pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
241+
pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
225242
self.imp.type_of_pat(pat)
226243
}
227244

228-
pub fn type_of_pat_with_coercion(&self, expr: &ast::Pat) -> Option<(Type, bool)> {
229-
self.imp.type_of_pat_with_coercion(expr)
230-
}
231-
232245
pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
233246
self.imp.type_of_self(param)
234247
}
@@ -565,20 +578,16 @@ impl<'db> SemanticsImpl<'db> {
565578
Type::new_with_resolver(self.db, &scope.resolver, ty)
566579
}
567580

568-
fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
569-
self.analyze(expr.syntax()).type_of_expr(self.db, expr)
570-
}
571-
572-
fn type_of_expr_with_coercion(&self, expr: &ast::Expr) -> Option<(Type, bool)> {
573-
self.analyze(expr.syntax()).type_of_expr_with_coercion(self.db, expr)
574-
}
575-
576-
fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
577-
self.analyze(pat.syntax()).type_of_pat(self.db, pat)
581+
fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
582+
self.analyze(expr.syntax())
583+
.type_of_expr(self.db, expr)
584+
.map(|(ty, coerced)| TypeInfo { ty, coerced })
578585
}
579586

580-
fn type_of_pat_with_coercion(&self, pat: &ast::Pat) -> Option<(Type, bool)> {
581-
self.analyze(pat.syntax()).type_of_pat_with_coercion(self.db, pat)
587+
fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
588+
self.analyze(pat.syntax())
589+
.type_of_pat(self.db, pat)
590+
.map(|(ty, coerced)| TypeInfo { ty, coerced })
582591
}
583592

584593
fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
@@ -757,7 +766,7 @@ impl<'db> SemanticsImpl<'db> {
757766
ast::Expr::FieldExpr(field_expr) => field_expr,
758767
_ => return None,
759768
};
760-
let ty = self.type_of_expr(&field_expr.expr()?)?;
769+
let ty = self.type_of_expr(&field_expr.expr()?)?.ty;
761770
if !ty.is_packed(self.db) {
762771
return None;
763772
}
@@ -784,7 +793,7 @@ impl<'db> SemanticsImpl<'db> {
784793
self.type_of_expr(&expr)
785794
})
786795
// Binding a reference to a packed type is possibly unsafe.
787-
.map(|ty| ty.is_packed(self.db))
796+
.map(|ty| ty.ty.is_packed(self.db))
788797
.unwrap_or(false)
789798

790799
// FIXME This needs layout computation to be correct. It will highlight
@@ -830,7 +839,7 @@ impl<'db> SemanticsImpl<'db> {
830839
}
831840
})
832841
// Binding a reference to a packed type is possibly unsafe.
833-
.map(|ty| ty.is_packed(self.db))
842+
.map(|ty| ty.ty.is_packed(self.db))
834843
.unwrap_or(false)
835844
}
836845
}

crates/hir/src/source_analyzer.rs

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -116,46 +116,36 @@ impl SourceAnalyzer {
116116
Some(res)
117117
}
118118

119-
pub(crate) fn type_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<Type> {
120-
let expr_id = self.expr_id(db, expr)?;
121-
let ty = self.infer.as_ref()?[expr_id].clone();
122-
Type::new_with_resolver(db, &self.resolver, ty)
123-
}
124-
125-
pub(crate) fn type_of_expr_with_coercion(
119+
pub(crate) fn type_of_expr(
126120
&self,
127121
db: &dyn HirDatabase,
128122
expr: &ast::Expr,
129-
) -> Option<(Type, bool)> {
123+
) -> Option<(Type, Option<Type>)> {
130124
let expr_id = self.expr_id(db, expr)?;
131125
let infer = self.infer.as_ref()?;
132-
let (ty, coerced) = infer
126+
let coerced = infer
133127
.expr_adjustments
134128
.get(&expr_id)
135-
.and_then(|adjusts| adjusts.last().map(|adjust| (&adjust.target, true)))
136-
.unwrap_or_else(|| (&infer[expr_id], false));
137-
Type::new_with_resolver(db, &self.resolver, ty.clone()).zip(Some(coerced))
138-
}
139-
140-
pub(crate) fn type_of_pat(&self, db: &dyn HirDatabase, pat: &ast::Pat) -> Option<Type> {
141-
let pat_id = self.pat_id(pat)?;
142-
let ty = self.infer.as_ref()?[pat_id].clone();
143-
Type::new_with_resolver(db, &self.resolver, ty)
129+
.and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone()));
130+
let ty = infer[expr_id].clone();
131+
let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
132+
mk_ty(ty.clone()).zip(Some(coerced.and_then(mk_ty)))
144133
}
145134

146-
pub(crate) fn type_of_pat_with_coercion(
135+
pub(crate) fn type_of_pat(
147136
&self,
148137
db: &dyn HirDatabase,
149138
pat: &ast::Pat,
150-
) -> Option<(Type, bool)> {
139+
) -> Option<(Type, Option<Type>)> {
151140
let pat_id = self.pat_id(pat)?;
152141
let infer = self.infer.as_ref()?;
153-
let (ty, coerced) = infer
142+
let coerced = infer
154143
.pat_adjustments
155144
.get(&pat_id)
156-
.and_then(|adjusts| adjusts.last().map(|adjust| (&adjust.target, true)))
157-
.unwrap_or_else(|| (&infer[pat_id], false));
158-
Type::new_with_resolver(db, &self.resolver, ty.clone()).zip(Some(coerced))
145+
.and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone()));
146+
let ty = infer[pat_id].clone();
147+
let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
148+
mk_ty(ty.clone()).zip(Some(coerced.and_then(mk_ty)))
159149
}
160150

161151
pub(crate) fn type_of_self(

crates/ide/src/call_hierarchy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
8686
let name_ref = call_node.name_ref()?;
8787
let func_target = match call_node {
8888
FnCallNode::CallExpr(expr) => {
89-
let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?;
89+
let callable = sema.type_of_expr(&expr.expr()?)?.ty.as_callable(db)?;
9090
match callable.kind() {
9191
hir::CallableKind::Function(it) => it.try_to_nav(db),
9292
_ => None,

crates/ide/src/goto_type_definition.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ pub(crate) fn goto_type_definition(
3232
let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| {
3333
let ty = match_ast! {
3434
match node {
35-
ast::Expr(it) => sema.type_of_expr(&it)?,
36-
ast::Pat(it) => sema.type_of_pat(&it)?,
35+
ast::Expr(it) => sema.type_of_expr(&it)?.ty,
36+
ast::Pat(it) => sema.type_of_pat(&it)?.ty,
3737
ast::SelfParam(it) => sema.type_of_self(&it)?,
3838
ast::Type(it) => sema.resolve_type(&it)?,
3939
ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,

crates/ide/src/highlight_related.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ fn highlight_exit_points(
123123
}
124124
}
125125
ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroCall(_) => {
126-
if sema.type_of_expr(&expr).map_or(false, |ty| ty.is_never()) {
126+
if sema.type_of_expr(&expr).map_or(false, |ty| ty.ty.is_never()) {
127127
highlights
128128
.push(HighlightedRange { access: None, range: expr.syntax().text_range() });
129129
}

crates/ide/src/hover.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use either::Either;
2-
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
2+
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
33
use ide_db::{
44
base_db::{FileRange, SourceDatabase},
55
defs::{Definition, NameClass, NameRefClass},
@@ -225,19 +225,15 @@ fn hover_type_info(
225225
config: &HoverConfig,
226226
expr_or_pat: &Either<ast::Expr, ast::Pat>,
227227
) -> Option<HoverResult> {
228-
let (ty, coerced) = match expr_or_pat {
229-
Either::Left(expr) => sema.type_of_expr_with_coercion(expr)?,
230-
Either::Right(pat) => sema.type_of_pat_with_coercion(pat)?,
228+
let TypeInfo { ty, coerced } = match expr_or_pat {
229+
Either::Left(expr) => sema.type_of_expr(expr)?,
230+
Either::Right(pat) => sema.type_of_pat(pat)?,
231231
};
232232

233233
let mut res = HoverResult::default();
234-
res.markup = if coerced {
235-
let uncoerced_ty = match expr_or_pat {
236-
Either::Left(expr) => sema.type_of_expr(expr)?,
237-
Either::Right(pat) => sema.type_of_pat(pat)?,
238-
};
239-
let uncoerced = uncoerced_ty.display(sema.db).to_string();
240-
let coerced = ty.display(sema.db).to_string();
234+
res.markup = if let Some(coerced_ty) = coerced {
235+
let uncoerced = ty.display(sema.db).to_string();
236+
let coerced = coerced_ty.display(sema.db).to_string();
241237
format!(
242238
"```text\nType: {:>upad$}\nCoerced to: {:>cpad$}\n```\n",
243239
uncoerced = uncoerced,

crates/ide/src/inlay_hints.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use either::Either;
2-
use hir::{known, Callable, HasVisibility, HirDisplay, Semantics};
2+
use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
33
use ide_db::helpers::FamousDefs;
44
use ide_db::RootDatabase;
55
use stdx::to_lower_snake_case;
@@ -117,7 +117,7 @@ fn get_chaining_hints(
117117
next_next = tokens.next()?.kind();
118118
}
119119
if next_next == T![.] {
120-
let ty = sema.type_of_expr(&expr)?;
120+
let ty = sema.type_of_expr(&expr)?.ty;
121121
if ty.is_unknown() {
122122
return None;
123123
}
@@ -189,7 +189,7 @@ fn get_bind_pat_hints(
189189
let krate = sema.scope(pat.syntax()).module().map(|it| it.krate());
190190
let famous_defs = FamousDefs(sema, krate);
191191

192-
let ty = sema.type_of_pat(&pat.clone().into())?;
192+
let ty = sema.type_of_pat(&pat.clone().into())?.ty;
193193

194194
if should_not_display_type_hint(sema, &pat, &ty) {
195195
return None;
@@ -308,6 +308,7 @@ fn should_not_display_type_hint(
308308
return it.in_token().is_none() ||
309309
it.iterable()
310310
.and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
311+
.map(TypeInfo::ty)
311312
.map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
312313
},
313314
_ => (),
@@ -393,7 +394,7 @@ fn is_enum_name_similar_to_param_name(
393394
argument: &ast::Expr,
394395
param_name: &str,
395396
) -> bool {
396-
match sema.type_of_expr(argument).and_then(|t| t.as_adt()) {
397+
match sema.type_of_expr(argument).and_then(|t| t.ty.as_adt()) {
397398
Some(hir::Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name,
398399
_ => false,
399400
}
@@ -430,7 +431,7 @@ fn get_callable(
430431
) -> Option<(hir::Callable, ast::ArgList)> {
431432
match expr {
432433
ast::Expr::CallExpr(expr) => {
433-
sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db).zip(expr.arg_list())
434+
sema.type_of_expr(&expr.expr()?)?.ty.as_callable(sema.db).zip(expr.arg_list())
434435
}
435436
ast::Expr::MethodCallExpr(expr) => {
436437
sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())

crates/ide/src/syntax_highlighting/highlight.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ pub(super) fn element(
123123
let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
124124

125125
let expr = prefix_expr.expr()?;
126-
let ty = sema.type_of_expr(&expr)?;
126+
let ty = sema.type_of_expr(&expr)?.ty;
127127
if ty.is_raw_ptr() {
128128
HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
129129
} else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
@@ -555,7 +555,7 @@ fn highlight_method_call(
555555
if let Some(receiver_ty) =
556556
method_call.receiver().and_then(|it| sema.type_of_expr(&it))
557557
{
558-
if !receiver_ty.is_copy(sema.db) {
558+
if !receiver_ty.ty.is_copy(sema.db) {
559559
h |= HlMod::Consuming
560560
}
561561
}

crates/ide_assists/src/handlers/add_explicit_type.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
5454
}
5555

5656
let ty = match (pat, expr) {
57-
(ast::Pat::IdentPat(_), Some(expr)) => ctx.sema.type_of_expr_with_coercion(&expr)?.0,
57+
(ast::Pat::IdentPat(_), Some(expr)) => ctx.sema.type_of_expr(&expr)?,
5858
(pat, _) => ctx.sema.type_of_pat(&pat)?,
59-
};
59+
}
60+
.coerced_or_original();
6061

6162
// Unresolved or unnameable types can't be annotated
6263
if ty.contains_unknown() || ty.is_closure() {

0 commit comments

Comments
 (0)