9765: internal: Introduce TypeInfo r=Veykril a=Veykril



Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-08-03 15:32:51 +00:00 committed by GitHub
commit 00c8cab20b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 141 additions and 135 deletions

View File

@ -89,7 +89,7 @@ pub use crate::{
UnresolvedModule, UnresolvedProcMacro, UnresolvedModule, UnresolvedProcMacro,
}, },
has_source::HasSource, has_source::HasSource,
semantics::{PathResolution, Semantics, SemanticsScope}, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
}; };
// Be careful with these re-exports. // Be careful with these re-exports.

View File

@ -87,6 +87,29 @@ impl PathResolution {
} }
} }
#[derive(Debug)]
pub struct TypeInfo {
/// The original type of the expression or pattern.
pub original: Type,
/// The adjusted type, if an adjustment happened.
pub adjusted: Option<Type>,
}
impl TypeInfo {
pub fn original(self) -> Type {
self.original
}
pub fn has_adjustment(&self) -> bool {
self.adjusted.is_some()
}
/// The adjusted type, or the original in case no adjustments occurred.
pub fn adjusted(self) -> Type {
self.adjusted.unwrap_or(self.original)
}
}
/// Primary API to get semantic information, like types, from syntax trees. /// Primary API to get semantic information, like types, from syntax trees.
pub struct Semantics<'db, DB> { pub struct Semantics<'db, DB> {
pub db: &'db DB, pub db: &'db DB,
@ -212,23 +235,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.resolve_type(ty) self.imp.resolve_type(ty)
} }
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
self.imp.type_of_expr(expr) self.imp.type_of_expr(expr)
} }
/// Returns true in case a coercion happened. pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
pub fn type_of_expr_with_coercion(&self, expr: &ast::Expr) -> Option<(Type, bool)> {
self.imp.type_of_expr_with_coercion(expr)
}
pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
self.imp.type_of_pat(pat) self.imp.type_of_pat(pat)
} }
pub fn type_of_pat_with_coercion(&self, expr: &ast::Pat) -> Option<(Type, bool)> {
self.imp.type_of_pat_with_coercion(expr)
}
pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> { pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
self.imp.type_of_self(param) self.imp.type_of_self(param)
} }
@ -565,20 +579,16 @@ impl<'db> SemanticsImpl<'db> {
Type::new_with_resolver(self.db, &scope.resolver, ty) Type::new_with_resolver(self.db, &scope.resolver, ty)
} }
fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
self.analyze(expr.syntax()).type_of_expr(self.db, expr) self.analyze(expr.syntax())
.type_of_expr(self.db, expr)
.map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
} }
fn type_of_expr_with_coercion(&self, expr: &ast::Expr) -> Option<(Type, bool)> { fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
self.analyze(expr.syntax()).type_of_expr_with_coercion(self.db, expr) self.analyze(pat.syntax())
} .type_of_pat(self.db, pat)
.map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
self.analyze(pat.syntax()).type_of_pat(self.db, pat)
}
fn type_of_pat_with_coercion(&self, pat: &ast::Pat) -> Option<(Type, bool)> {
self.analyze(pat.syntax()).type_of_pat_with_coercion(self.db, pat)
} }
fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> { fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
@ -757,7 +767,7 @@ impl<'db> SemanticsImpl<'db> {
ast::Expr::FieldExpr(field_expr) => field_expr, ast::Expr::FieldExpr(field_expr) => field_expr,
_ => return None, _ => return None,
}; };
let ty = self.type_of_expr(&field_expr.expr()?)?; let ty = self.type_of_expr(&field_expr.expr()?)?.original;
if !ty.is_packed(self.db) { if !ty.is_packed(self.db) {
return None; return None;
} }
@ -784,7 +794,7 @@ impl<'db> SemanticsImpl<'db> {
self.type_of_expr(&expr) self.type_of_expr(&expr)
}) })
// Binding a reference to a packed type is possibly unsafe. // Binding a reference to a packed type is possibly unsafe.
.map(|ty| ty.is_packed(self.db)) .map(|ty| ty.original.is_packed(self.db))
.unwrap_or(false) .unwrap_or(false)
// FIXME This needs layout computation to be correct. It will highlight // FIXME This needs layout computation to be correct. It will highlight
@ -830,7 +840,7 @@ impl<'db> SemanticsImpl<'db> {
} }
}) })
// Binding a reference to a packed type is possibly unsafe. // Binding a reference to a packed type is possibly unsafe.
.map(|ty| ty.is_packed(self.db)) .map(|ty| ty.original.is_packed(self.db))
.unwrap_or(false) .unwrap_or(false)
} }
} }

View File

@ -116,46 +116,36 @@ impl SourceAnalyzer {
Some(res) Some(res)
} }
pub(crate) fn type_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<Type> { pub(crate) fn type_of_expr(
let expr_id = self.expr_id(db, expr)?;
let ty = self.infer.as_ref()?[expr_id].clone();
Type::new_with_resolver(db, &self.resolver, ty)
}
pub(crate) fn type_of_expr_with_coercion(
&self, &self,
db: &dyn HirDatabase, db: &dyn HirDatabase,
expr: &ast::Expr, expr: &ast::Expr,
) -> Option<(Type, bool)> { ) -> Option<(Type, Option<Type>)> {
let expr_id = self.expr_id(db, expr)?; let expr_id = self.expr_id(db, expr)?;
let infer = self.infer.as_ref()?; let infer = self.infer.as_ref()?;
let (ty, coerced) = infer let coerced = infer
.expr_adjustments .expr_adjustments
.get(&expr_id) .get(&expr_id)
.and_then(|adjusts| adjusts.last().map(|adjust| (&adjust.target, true))) .and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone()));
.unwrap_or_else(|| (&infer[expr_id], false)); let ty = infer[expr_id].clone();
Type::new_with_resolver(db, &self.resolver, ty.clone()).zip(Some(coerced)) let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
mk_ty(ty.clone()).zip(Some(coerced.and_then(mk_ty)))
} }
pub(crate) fn type_of_pat(&self, db: &dyn HirDatabase, pat: &ast::Pat) -> Option<Type> { pub(crate) fn type_of_pat(
let pat_id = self.pat_id(pat)?;
let ty = self.infer.as_ref()?[pat_id].clone();
Type::new_with_resolver(db, &self.resolver, ty)
}
pub(crate) fn type_of_pat_with_coercion(
&self, &self,
db: &dyn HirDatabase, db: &dyn HirDatabase,
pat: &ast::Pat, pat: &ast::Pat,
) -> Option<(Type, bool)> { ) -> Option<(Type, Option<Type>)> {
let pat_id = self.pat_id(pat)?; let pat_id = self.pat_id(pat)?;
let infer = self.infer.as_ref()?; let infer = self.infer.as_ref()?;
let (ty, coerced) = infer let coerced = infer
.pat_adjustments .pat_adjustments
.get(&pat_id) .get(&pat_id)
.and_then(|adjusts| adjusts.last().map(|adjust| (&adjust.target, true))) .and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone()));
.unwrap_or_else(|| (&infer[pat_id], false)); let ty = infer[pat_id].clone();
Type::new_with_resolver(db, &self.resolver, ty.clone()).zip(Some(coerced)) let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
mk_ty(ty.clone()).zip(Some(coerced.and_then(mk_ty)))
} }
pub(crate) fn type_of_self( pub(crate) fn type_of_self(

View File

@ -86,7 +86,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
let name_ref = call_node.name_ref()?; let name_ref = call_node.name_ref()?;
let func_target = match call_node { let func_target = match call_node {
FnCallNode::CallExpr(expr) => { FnCallNode::CallExpr(expr) => {
let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?; let callable = sema.type_of_expr(&expr.expr()?)?.original.as_callable(db)?;
match callable.kind() { match callable.kind() {
hir::CallableKind::Function(it) => it.try_to_nav(db), hir::CallableKind::Function(it) => it.try_to_nav(db),
_ => None, _ => None,

View File

@ -32,8 +32,8 @@ pub(crate) fn goto_type_definition(
let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| { let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| {
let ty = match_ast! { let ty = match_ast! {
match node { match node {
ast::Expr(it) => sema.type_of_expr(&it)?, ast::Expr(it) => sema.type_of_expr(&it)?.original,
ast::Pat(it) => sema.type_of_pat(&it)?, ast::Pat(it) => sema.type_of_pat(&it)?.original,
ast::SelfParam(it) => sema.type_of_self(&it)?, ast::SelfParam(it) => sema.type_of_self(&it)?,
ast::Type(it) => sema.resolve_type(&it)?, ast::Type(it) => sema.resolve_type(&it)?,
ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?, ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,

View File

@ -123,7 +123,7 @@ fn highlight_exit_points(
} }
} }
ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroCall(_) => { ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroCall(_) => {
if sema.type_of_expr(&expr).map_or(false, |ty| ty.is_never()) { if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) {
highlights highlights
.push(HighlightedRange { access: None, range: expr.syntax().text_range() }); .push(HighlightedRange { access: None, range: expr.syntax().text_range() });
} }

View File

@ -1,5 +1,5 @@
use either::Either; use either::Either;
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
use ide_db::{ use ide_db::{
base_db::{FileRange, SourceDatabase}, base_db::{FileRange, SourceDatabase},
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
@ -225,33 +225,29 @@ fn hover_type_info(
config: &HoverConfig, config: &HoverConfig,
expr_or_pat: &Either<ast::Expr, ast::Pat>, expr_or_pat: &Either<ast::Expr, ast::Pat>,
) -> Option<HoverResult> { ) -> Option<HoverResult> {
let (ty, coerced) = match expr_or_pat { let TypeInfo { original, adjusted } = match expr_or_pat {
Either::Left(expr) => sema.type_of_expr_with_coercion(expr)?, Either::Left(expr) => sema.type_of_expr(expr)?,
Either::Right(pat) => sema.type_of_pat_with_coercion(pat)?, Either::Right(pat) => sema.type_of_pat(pat)?,
}; };
let mut res = HoverResult::default(); let mut res = HoverResult::default();
res.markup = if coerced { res.markup = if let Some(adjusted_ty) = adjusted {
let uncoerced_ty = match expr_or_pat { let original = original.display(sema.db).to_string();
Either::Left(expr) => sema.type_of_expr(expr)?, let adjusted = adjusted_ty.display(sema.db).to_string();
Either::Right(pat) => sema.type_of_pat(pat)?,
};
let uncoerced = uncoerced_ty.display(sema.db).to_string();
let coerced = ty.display(sema.db).to_string();
format!( format!(
"```text\nType: {:>upad$}\nCoerced to: {:>cpad$}\n```\n", "```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n```\n",
uncoerced = uncoerced, uncoerced = original,
coerced = coerced, coerced = adjusted,
// 6 base padding for static text prefix of each line // 6 base padding for static text prefix of each line
upad = 6 + coerced.len().max(uncoerced.len()), apad = 6 + adjusted.len().max(original.len()),
cpad = uncoerced.len(), opad = original.len(),
) )
.into() .into()
} else { } else {
if config.markdown() { if config.markdown() {
Markup::fenced_block(&ty.display(sema.db)) Markup::fenced_block(&original.display(sema.db))
} else { } else {
ty.display(sema.db).to_string().into() original.display(sema.db).to_string().into()
} }
}; };
Some(res) Some(res)

View File

@ -1,5 +1,5 @@
use either::Either; use either::Either;
use hir::{known, Callable, HasVisibility, HirDisplay, Semantics}; use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
use ide_db::helpers::FamousDefs; use ide_db::helpers::FamousDefs;
use ide_db::RootDatabase; use ide_db::RootDatabase;
use stdx::to_lower_snake_case; use stdx::to_lower_snake_case;
@ -117,7 +117,7 @@ fn get_chaining_hints(
next_next = tokens.next()?.kind(); next_next = tokens.next()?.kind();
} }
if next_next == T![.] { if next_next == T![.] {
let ty = sema.type_of_expr(&expr)?; let ty = sema.type_of_expr(&expr)?.original;
if ty.is_unknown() { if ty.is_unknown() {
return None; return None;
} }
@ -189,7 +189,7 @@ fn get_bind_pat_hints(
let krate = sema.scope(pat.syntax()).module().map(|it| it.krate()); let krate = sema.scope(pat.syntax()).module().map(|it| it.krate());
let famous_defs = FamousDefs(sema, krate); let famous_defs = FamousDefs(sema, krate);
let ty = sema.type_of_pat(&pat.clone().into())?; let ty = sema.type_of_pat(&pat.clone().into())?.original;
if should_not_display_type_hint(sema, &pat, &ty) { if should_not_display_type_hint(sema, &pat, &ty) {
return None; return None;
@ -308,6 +308,7 @@ fn should_not_display_type_hint(
return it.in_token().is_none() || return it.in_token().is_none() ||
it.iterable() it.iterable()
.and_then(|iterable_expr| sema.type_of_expr(&iterable_expr)) .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
.map(TypeInfo::original)
.map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit()) .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
}, },
_ => (), _ => (),
@ -393,7 +394,7 @@ fn is_enum_name_similar_to_param_name(
argument: &ast::Expr, argument: &ast::Expr,
param_name: &str, param_name: &str,
) -> bool { ) -> bool {
match sema.type_of_expr(argument).and_then(|t| t.as_adt()) { match sema.type_of_expr(argument).and_then(|t| t.original.as_adt()) {
Some(hir::Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name, Some(hir::Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name,
_ => false, _ => false,
} }
@ -430,7 +431,7 @@ fn get_callable(
) -> Option<(hir::Callable, ast::ArgList)> { ) -> Option<(hir::Callable, ast::ArgList)> {
match expr { match expr {
ast::Expr::CallExpr(expr) => { ast::Expr::CallExpr(expr) => {
sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db).zip(expr.arg_list()) sema.type_of_expr(&expr.expr()?)?.original.as_callable(sema.db).zip(expr.arg_list())
} }
ast::Expr::MethodCallExpr(expr) => { ast::Expr::MethodCallExpr(expr) => {
sema.resolve_method_call_as_callable(expr).zip(expr.arg_list()) sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())

View File

@ -123,7 +123,7 @@ pub(super) fn element(
let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
let expr = prefix_expr.expr()?; let expr = prefix_expr.expr()?;
let ty = sema.type_of_expr(&expr)?; let ty = sema.type_of_expr(&expr)?.original;
if ty.is_raw_ptr() { if ty.is_raw_ptr() {
HlTag::Operator(HlOperator::Other) | HlMod::Unsafe HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
} else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() { } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
@ -555,7 +555,7 @@ fn highlight_method_call(
if let Some(receiver_ty) = if let Some(receiver_ty) =
method_call.receiver().and_then(|it| sema.type_of_expr(&it)) method_call.receiver().and_then(|it| sema.type_of_expr(&it))
{ {
if !receiver_ty.is_copy(sema.db) { if !receiver_ty.adjusted().is_copy(sema.db) {
h |= HlMod::Consuming h |= HlMod::Consuming
} }
} }

View File

@ -54,9 +54,10 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
} }
let ty = match (pat, expr) { let ty = match (pat, expr) {
(ast::Pat::IdentPat(_), Some(expr)) => ctx.sema.type_of_expr_with_coercion(&expr)?.0, (ast::Pat::IdentPat(_), Some(expr)) => ctx.sema.type_of_expr(&expr)?,
(pat, _) => ctx.sema.type_of_pat(&pat)?, (pat, _) => ctx.sema.type_of_pat(&pat)?,
}; }
.adjusted();
// Unresolved or unnameable types can't be annotated // Unresolved or unnameable types can't be annotated
if ty.contains_unknown() || ty.is_closure() { if ty.contains_unknown() || ty.is_closure() {

View File

@ -86,7 +86,7 @@ fn validate_method_call_expr(
let receiver = expr.receiver()?; let receiver = expr.receiver()?;
let expr = ast::Expr::MethodCallExpr(expr); let expr = ast::Expr::MethodCallExpr(expr);
let it_type = sema.type_of_expr(&receiver)?; let it_type = sema.type_of_expr(&receiver)?.adjusted();
let module = sema.scope(receiver.syntax()).module()?; let module = sema.scope(receiver.syntax()).module()?;
let krate = module.krate(); let krate = module.krate();

View File

@ -2,7 +2,7 @@ use std::{hash::BuildHasherDefault, iter};
use ast::make; use ast::make;
use either::Either; use either::Either;
use hir::{HirDisplay, Local, Semantics}; use hir::{HirDisplay, Local, Semantics, TypeInfo};
use ide_db::{ use ide_db::{
defs::{Definition, NameRefClass}, defs::{Definition, NameRefClass},
search::{FileReference, ReferenceAccess, SearchScope}, search::{FileReference, ReferenceAccess, SearchScope},
@ -344,7 +344,9 @@ impl FlowKind {
match self { match self {
FlowKind::Return(Some(expr)) FlowKind::Return(Some(expr))
| FlowKind::Break(Some(expr)) | FlowKind::Break(Some(expr))
| FlowKind::TryReturn { expr, .. } => ctx.sema.type_of_expr(expr), | FlowKind::TryReturn { expr, .. } => {
ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted)
}
FlowKind::Try { .. } => { FlowKind::Try { .. } => {
stdx::never!("try does not have defined expr_ty"); stdx::never!("try does not have defined expr_ty");
None None
@ -850,10 +852,7 @@ fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode {
fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> { fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> {
match body.tail_expr() { match body.tail_expr() {
Some(expr) => { Some(expr) => ctx.sema.type_of_expr(&expr).map(TypeInfo::original).map(RetType::Expr),
let ty = ctx.sema.type_of_expr(&expr)?;
Some(RetType::Expr(ty))
}
None => Some(RetType::Stmt), None => Some(RetType::Stmt),
} }
} }
@ -950,7 +949,7 @@ fn expr_err_kind(expr: &ast::Expr, ctx: &AssistContext) -> Option<TryKind> {
let text = func_name.syntax().text(); let text = func_name.syntax().text();
if text == "Err" { if text == "Err" {
Some(TryKind::Result { ty: ctx.sema.type_of_expr(expr)? }) Some(TryKind::Result { ty: ctx.sema.type_of_expr(expr).map(TypeInfo::original)? })
} else if text == "None" { } else if text == "None" {
Some(TryKind::Option) Some(TryKind::Option)
} else { } else {

View File

@ -39,8 +39,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
.ancestors() .ancestors()
.take_while(|it| it.text_range().contains_range(ctx.frange.range)) .take_while(|it| it.text_range().contains_range(ctx.frange.range))
.find_map(valid_target_expr)?; .find_map(valid_target_expr)?;
if let Some(ty) = ctx.sema.type_of_expr(&to_extract) { if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) {
if ty.is_unit() { if ty_info.adjusted().is_unit() {
return None; return None;
} }
} }

View File

@ -223,7 +223,7 @@ impl ExtendedEnum {
} }
fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> { fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
sema.type_of_expr(expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { sema.type_of_expr(expr)?.adjusted().autoderef(sema.db).find_map(|ty| match ty.as_adt() {
Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)), Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
_ => ty.is_bool().then(|| ExtendedEnum::Bool), _ => ty.is_bool().then(|| ExtendedEnum::Bool),
}) })
@ -234,6 +234,7 @@ fn resolve_tuple_of_enum_def(
expr: &ast::Expr, expr: &ast::Expr,
) -> Option<Vec<ExtendedEnum>> { ) -> Option<Vec<ExtendedEnum>> {
sema.type_of_expr(expr)? sema.type_of_expr(expr)?
.adjusted()
.tuple_fields(sema.db) .tuple_fields(sema.db)
.iter() .iter()
.map(|ty| { .map(|ty| {

View File

@ -1,4 +1,4 @@
use hir::HirDisplay; use hir::{HirDisplay, TypeInfo};
use ide_db::{base_db::FileId, helpers::SnippetCap}; use ide_db::{base_db::FileId, helpers::SnippetCap};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use stdx::to_lower_snake_case; use stdx::to_lower_snake_case;
@ -153,7 +153,8 @@ impl FunctionBuilder {
// type, but that the current state of their code doesn't allow that return type // type, but that the current state of their code doesn't allow that return type
// to be accurately inferred. // to be accurately inferred.
let (ret_ty, should_render_snippet) = { let (ret_ty, should_render_snippet) = {
match ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone())) { match ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone())).map(TypeInfo::original)
{
Some(ty) if ty.is_unknown() || ty.is_unit() => (make::ty_unit(), true), Some(ty) if ty.is_unknown() || ty.is_unit() => (make::ty_unit(), true),
Some(ty) => { Some(ty) => {
let rendered = ty.display_source_code(ctx.db(), target_module.into()); let rendered = ty.display_source_code(ctx.db(), target_module.into());
@ -331,7 +332,7 @@ fn fn_arg_type(
target_module: hir::Module, target_module: hir::Module,
fn_arg: &ast::Expr, fn_arg: &ast::Expr,
) -> Option<String> { ) -> Option<String> {
let ty = ctx.sema.type_of_expr(fn_arg)?; let ty = ctx.sema.type_of_expr(fn_arg)?.adjusted();
if ty.is_unknown() { if ty.is_unknown() {
return None; return None;
} }

View File

@ -18,7 +18,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?; let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?;
let module = ctx.sema.scope(tail_expr.syntax()).module()?; let module = ctx.sema.scope(tail_expr.syntax()).module()?;
let ty = ctx.sema.type_of_expr(&tail_expr)?; let ty = ctx.sema.type_of_expr(&tail_expr)?.adjusted();
if ty.is_unit() { if ty.is_unit() {
return None; return None;
} }

View File

@ -1,5 +1,5 @@
use ast::make; use ast::make;
use hir::{HasSource, PathResolution}; use hir::{HasSource, PathResolution, TypeInfo};
use ide_db::{defs::Definition, search::FileReference}; use ide_db::{defs::Definition, search::FileReference};
use itertools::izip; use itertools::izip;
use syntax::{ use syntax::{
@ -189,10 +189,9 @@ pub(crate) fn inline_(
_ => { _ => {
let ty = ctx let ty = ctx
.sema .sema
.type_of_expr_with_coercion(&expr) .type_of_expr(&expr)
.map_or(false, |(_, coerced)| coerced) .filter(TypeInfo::has_adjustment)
.then(|| param_ty) .and_then(|_| param_ty);
.flatten();
body.push_front( body.push_front(
make::let_stmt(pat, ty, Some(expr)).clone_for_update().into(), make::let_stmt(pat, ty, Some(expr)).clone_for_update().into(),
) )

View File

@ -80,13 +80,13 @@ fn is_ref_and_impls_iter_method(
}; };
let wanted_method = if ref_expr.mut_token().is_some() { known::iter_mut } else { known::iter }; let wanted_method = if ref_expr.mut_token().is_some() { known::iter_mut } else { known::iter };
let expr_behind_ref = ref_expr.expr()?; let expr_behind_ref = ref_expr.expr()?;
let typ = sema.type_of_expr(&expr_behind_ref)?; let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
let scope = sema.scope(iterable.syntax()); let scope = sema.scope(iterable.syntax());
let krate = scope.module()?.krate(); let krate = scope.module()?.krate();
let traits_in_scope = scope.traits_in_scope(); let traits_in_scope = scope.traits_in_scope();
let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?; let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
let has_wanted_method = typ let has_wanted_method = ty
.iterate_method_candidates( .iterate_method_candidates(
sema.db, sema.db,
krate, krate,
@ -110,7 +110,7 @@ fn is_ref_and_impls_iter_method(
/// Whether iterable implements core::Iterator /// Whether iterable implements core::Iterator
fn impls_core_iter(sema: &hir::Semantics<ide_db::RootDatabase>, iterable: &ast::Expr) -> bool { fn impls_core_iter(sema: &hir::Semantics<ide_db::RootDatabase>, iterable: &ast::Expr) -> bool {
let it_typ = match sema.type_of_expr(iterable) { let it_typ = match sema.type_of_expr(iterable) {
Some(it) => it, Some(it) => it.adjusted(),
None => return false, None => return false,
}; };

View File

@ -127,7 +127,7 @@ fn make_else_arm(
let pattern = if let [(Either::Left(pat), _)] = conditionals { let pattern = if let [(Either::Left(pat), _)] = conditionals {
ctx.sema ctx.sema
.type_of_pat(&pat) .type_of_pat(&pat)
.and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))
.zip(Some(pat)) .zip(Some(pat))
} else { } else {
None None
@ -268,7 +268,7 @@ fn binds_name(pat: &ast::Pat) -> bool {
fn is_sad_pat(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool { fn is_sad_pat(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool {
sema.type_of_pat(pat) sema.type_of_pat(pat)
.and_then(|ty| TryEnum::from_ty(sema, &ty)) .and_then(|ty| TryEnum::from_ty(sema, &ty.adjusted()))
.map_or(false, |it| does_pat_match_variant(pat, &it.sad_pattern())) .map_or(false, |it| does_pat_match_variant(pat, &it.sad_pattern()))
} }

View File

@ -49,8 +49,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
target, target,
|edit| { |edit| {
let ty = ctx.sema.type_of_expr(&init); let ty = ctx.sema.type_of_expr(&init);
let happy_variant = let happy_variant = ty
ty.and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)).map(|it| it.happy_case()); .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))
.map(|it| it.happy_case());
let pat = match happy_variant { let pat = match happy_variant {
None => original_pat, None => original_pat,
Some(var_name) => { Some(var_name) => {

View File

@ -270,8 +270,8 @@ fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Opti
fn bin_impls_ord(sema: &Semantics<RootDatabase>, bin: &ast::BinExpr) -> bool { fn bin_impls_ord(sema: &Semantics<RootDatabase>, bin: &ast::BinExpr) -> bool {
match ( match (
bin.lhs().and_then(|lhs| sema.type_of_expr(&lhs)), bin.lhs().and_then(|lhs| sema.type_of_expr(&lhs)).map(hir::TypeInfo::adjusted),
bin.rhs().and_then(|rhs| sema.type_of_expr(&rhs)), bin.rhs().and_then(|rhs| sema.type_of_expr(&rhs)).map(hir::TypeInfo::adjusted),
) { ) {
(Some(lhs_ty), Some(rhs_ty)) if lhs_ty == rhs_ty => { (Some(lhs_ty), Some(rhs_ty)) if lhs_ty == rhs_ty => {
let krate = sema.scope(bin.syntax()).module().map(|it| it.krate()); let krate = sema.scope(bin.syntax()).module().map(|it| it.krate());

View File

@ -197,7 +197,7 @@ fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<St
match args_parent { match args_parent {
ast::CallExpr(call) => { ast::CallExpr(call) => {
let func = call.expr()?; let func = call.expr()?;
let func_ty = sema.type_of_expr(&func)?; let func_ty = sema.type_of_expr(&func)?.adjusted();
func_ty.as_callable(sema.db)? func_ty.as_callable(sema.db)?
}, },
ast::MethodCallExpr(method) => sema.resolve_method_call_as_callable(&method)?, ast::MethodCallExpr(method) => sema.resolve_method_call_as_callable(&method)?,
@ -225,7 +225,7 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
} }
fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
let ty = sema.type_of_expr(expr)?; let ty = sema.type_of_expr(expr)?.adjusted();
let ty = ty.remove_ref().unwrap_or(ty); let ty = ty.remove_ref().unwrap_or(ty);
name_of_type(&ty, sema.db) name_of_type(&ty, sema.db)

View File

@ -14,7 +14,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
}; };
let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) { let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) {
Some(ty) => ty, Some(ty) => ty.original,
_ => return, _ => return,
}; };

View File

@ -180,7 +180,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
if let Some(dot_receiver) = ctx.dot_receiver() { if let Some(dot_receiver) = ctx.dot_receiver() {
ImportAssets::for_fuzzy_method_call( ImportAssets::for_fuzzy_method_call(
current_module, current_module,
ctx.sema.type_of_expr(dot_receiver)?, ctx.sema.type_of_expr(dot_receiver)?.original,
fuzzy_name, fuzzy_name,
dot_receiver.syntax().clone(), dot_receiver.syntax().clone(),
) )

View File

@ -38,7 +38,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal); let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) { let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) {
Some(it) => it, Some(it) => it.original,
None => return, None => return,
}; };

View File

@ -12,9 +12,9 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
Some(ImmediateLocation::RecordExpr(record_expr)) => { Some(ImmediateLocation::RecordExpr(record_expr)) => {
let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone())); let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default(); let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
let impl_default_trait = default_trait let impl_default_trait = default_trait.zip(ty).map_or(false, |(default_trait, ty)| {
.zip(ty) ty.original.impls_trait(ctx.db, default_trait, &[])
.map_or(false, |(default_trait, ty)| ty.impls_trait(ctx.db, default_trait, &[])); });
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr); let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
if impl_default_trait && !missing_fields.is_empty() { if impl_default_trait && !missing_fields.is_empty() {

View File

@ -1,7 +1,7 @@
//! See `CompletionContext` structure. //! See `CompletionContext` structure.
use base_db::SourceDatabaseExt; use base_db::SourceDatabaseExt;
use hir::{Local, Name, ScopeDef, Semantics, SemanticsScope, Type}; use hir::{Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
use ide_db::{ use ide_db::{
base_db::{FilePosition, SourceDatabase}, base_db::{FilePosition, SourceDatabase},
call_info::ActiveParameter, call_info::ActiveParameter,
@ -453,7 +453,8 @@ impl<'a> CompletionContext<'a> {
cov_mark::hit!(expected_type_let_without_leading_char); cov_mark::hit!(expected_type_let_without_leading_char);
let ty = it.pat() let ty = it.pat()
.and_then(|pat| self.sema.type_of_pat(&pat)) .and_then(|pat| self.sema.type_of_pat(&pat))
.or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it))); .or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it)))
.map(TypeInfo::original);
let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
ident.name().map(NameOrNameRef::Name) ident.name().map(NameOrNameRef::Name)
} else { } else {
@ -496,27 +497,27 @@ impl<'a> CompletionContext<'a> {
ast::RecordExprField(it) => { ast::RecordExprField(it) => {
cov_mark::hit!(expected_type_struct_field_with_leading_char); cov_mark::hit!(expected_type_struct_field_with_leading_char);
( (
it.expr().as_ref().and_then(|e| self.sema.type_of_expr(e)), it.expr().as_ref().and_then(|e| self.sema.type_of_expr(e)).map(TypeInfo::original),
it.field_name().map(NameOrNameRef::NameRef), it.field_name().map(NameOrNameRef::NameRef),
) )
}, },
ast::MatchExpr(it) => { ast::MatchExpr(it) => {
cov_mark::hit!(expected_type_match_arm_without_leading_char); cov_mark::hit!(expected_type_match_arm_without_leading_char);
let ty = it.expr() let ty = it.expr().and_then(|e| self.sema.type_of_expr(&e)).map(TypeInfo::original);
.and_then(|e| self.sema.type_of_expr(&e));
(ty, None) (ty, None)
}, },
ast::IfExpr(it) => { ast::IfExpr(it) => {
cov_mark::hit!(expected_type_if_let_without_leading_char); cov_mark::hit!(expected_type_if_let_without_leading_char);
let ty = it.condition() let ty = it.condition()
.and_then(|cond| cond.expr()) .and_then(|cond| cond.expr())
.and_then(|e| self.sema.type_of_expr(&e)); .and_then(|e| self.sema.type_of_expr(&e))
.map(TypeInfo::original);
(ty, None) (ty, None)
}, },
ast::IdentPat(it) => { ast::IdentPat(it) => {
cov_mark::hit!(expected_type_if_let_with_leading_char); cov_mark::hit!(expected_type_if_let_with_leading_char);
cov_mark::hit!(expected_type_match_arm_with_leading_char); cov_mark::hit!(expected_type_match_arm_with_leading_char);
let ty = self.sema.type_of_pat(&ast::Pat::from(it)); let ty = self.sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original);
(ty, None) (ty, None)
}, },
ast::Fn(it) => { ast::Fn(it) => {
@ -527,7 +528,7 @@ impl<'a> CompletionContext<'a> {
}, },
ast::ClosureExpr(it) => { ast::ClosureExpr(it) => {
let ty = self.sema.type_of_expr(&it.into()); let ty = self.sema.type_of_expr(&it.into());
ty.and_then(|ty| ty.as_callable(self.db)) ty.and_then(|ty| ty.original.as_callable(self.db))
.map(|c| (Some(c.return_type()), None)) .map(|c| (Some(c.return_type()), None))
.unwrap_or((None, None)) .unwrap_or((None, None))
}, },

View File

@ -118,7 +118,9 @@ fn call_info_impl(
let calling_node = FnCallNode::with_node(&token.parent()?)?; let calling_node = FnCallNode::with_node(&token.parent()?)?;
let callable = match &calling_node { let callable = match &calling_node {
FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?, FnCallNode::CallExpr(call) => {
sema.type_of_expr(&call.expr()?)?.adjusted().as_callable(sema.db)?
}
FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?, FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
}; };
let active_param = if let Some(arg_list) = calling_node.arg_list() { let active_param = if let Some(arg_list) = calling_node.arg_list() {

View File

@ -543,7 +543,7 @@ impl ImportCandidate {
match sema.resolve_method_call(method_call) { match sema.resolve_method_call(method_call) {
Some(_) => None, Some(_) => None,
None => Some(Self::TraitMethod(TraitImportCandidate { None => Some(Self::TraitMethod(TraitImportCandidate {
receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, receiver_ty: sema.type_of_expr(&method_call.receiver()?)?.adjusted(),
assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()), assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()),
})), })),
} }

View File

@ -1,4 +1,4 @@
use hir::db::AstDatabase; use hir::{db::AstDatabase, TypeInfo};
use ide_db::{assists::Assist, helpers::for_each_tail_expr, source_change::SourceChange}; use ide_db::{assists::Assist, helpers::for_each_tail_expr, source_change::SourceChange};
use syntax::AstNode; use syntax::AstNode;
use text_edit::TextEdit; use text_edit::TextEdit;
@ -35,7 +35,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingOkOrSomeInTailExpr) -> Op
let tail_expr_range = tail_expr.syntax().text_range(); let tail_expr_range = tail_expr.syntax().text_range();
let mut builder = TextEdit::builder(); let mut builder = TextEdit::builder();
for_each_tail_expr(&tail_expr, &mut |expr| { for_each_tail_expr(&tail_expr, &mut |expr| {
if ctx.sema.type_of_expr(expr).as_ref() != Some(&d.expected) { if ctx.sema.type_of_expr(expr).map(TypeInfo::original).as_ref() != Some(&d.expected) {
builder.insert(expr.syntax().text_range().start(), format!("{}(", d.required)); builder.insert(expr.syntax().text_range().start(), format!("{}(", d.required));
builder.insert(expr.syntax().text_range().end(), ")".to_string()); builder.insert(expr.syntax().text_range().end(), ")".to_string());
} }

View File

@ -62,7 +62,7 @@ fn missing_record_expr_field_fixes(
}; };
let def_file_id = def_file_id.original_file(sema.db); let def_file_id = def_file_id.original_file(sema.db);
let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?; let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?.adjusted();
if new_field_type.is_unknown() { if new_field_type.is_unknown() {
return None; return None;
} }

View File

@ -609,9 +609,13 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
expr: &ast::Expr, expr: &ast::Expr,
) -> Result<usize, MatchFailed> { ) -> Result<usize, MatchFailed> {
use hir::HirDisplay; use hir::HirDisplay;
let code_type = self.sema.type_of_expr(expr).ok_or_else(|| { let code_type = self
match_error!("Failed to get receiver type for `{}`", expr.syntax().text()) .sema
})?; .type_of_expr(expr)
.ok_or_else(|| {
match_error!("Failed to get receiver type for `{}`", expr.syntax().text())
})?
.original;
// Temporary needed to make the borrow checker happy. // Temporary needed to make the borrow checker happy.
let res = code_type let res = code_type
.autoderef(self.sema.db) .autoderef(self.sema.db)