Respect coercions in inline_call

This commit is contained in:
Lukas Wirth 2021-07-10 19:16:32 +02:00
parent 7e6f40b6f1
commit 9e1eb77f6b
5 changed files with 67 additions and 19 deletions

View file

@ -576,7 +576,7 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(pat.syntax()).type_of_pat(self.db, pat) self.analyze(pat.syntax()).type_of_pat(self.db, pat)
} }
fn type_of_pat_with_coercion(&self, pat: &ast::Pat)-> Option<(Type, Option<Type>)> { fn type_of_pat_with_coercion(&self, pat: &ast::Pat) -> Option<(Type, Option<Type>)> {
self.analyze(pat.syntax()).type_of_pat_with_coercion(self.db, pat) self.analyze(pat.syntax()).type_of_pat_with_coercion(self.db, pat)
} }

View file

@ -21,7 +21,7 @@ use hir_def::{
use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
use hir_ty::{ use hir_ty::{
diagnostics::{record_literal_missing_fields, record_pattern_missing_fields}, diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
InferenceResult, Interner, Substitution, TyExt, TyLoweringContext, InferenceResult, Interner, Substitution, Ty, TyExt, TyLoweringContext,
}; };
use syntax::{ use syntax::{
ast::{self, AstNode}, ast::{self, AstNode},
@ -129,12 +129,12 @@ impl SourceAnalyzer {
) -> Option<(Type, Option<Type>)> { ) -> 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 = infer let coerced = infer
.expr_adjustments .expr_adjustments
.get(&expr_id) .get(&expr_id)
.and_then(|adjusts| adjusts.last().map(|adjust| &adjust.target)); .and_then(|adjusts| adjusts.last().map(|adjust| &adjust.target));
let mk_ty = |ty: &hir_ty::Ty| Type::new_with_resolver(db, &self.resolver, ty.clone()); let mk_ty = |ty: &Ty| Type::new_with_resolver(db, &self.resolver, ty.clone());
mk_ty(&infer[expr_id]).map(|it| (it, ty.and_then(mk_ty))) mk_ty(&infer[expr_id]).map(|ty| (ty, 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(&self, db: &dyn HirDatabase, pat: &ast::Pat) -> Option<Type> {
@ -150,12 +150,12 @@ impl SourceAnalyzer {
) -> Option<(Type, Option<Type>)> { ) -> 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 = infer let coerced = infer
.pat_adjustments .pat_adjustments
.get(&pat_id) .get(&pat_id)
.and_then(|adjusts| adjusts.last().map(|adjust| &adjust.target)); .and_then(|adjusts| adjusts.last().map(|adjust| &adjust.target));
let mk_ty = |ty: &hir_ty::Ty| Type::new_with_resolver(db, &self.resolver, ty.clone()); let mk_ty = |ty: &Ty| Type::new_with_resolver(db, &self.resolver, ty.clone());
mk_ty(&infer[pat_id]).map(|it| (it, ty.and_then(mk_ty))) mk_ty(&infer[pat_id]).map(|ty| (ty, coerced.and_then(mk_ty)))
} }
pub(crate) fn type_of_self( pub(crate) fn type_of_self(

View file

@ -146,7 +146,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
}; };
let let_stmt = make::let_stmt(bound_ident, Some(match_expr)); let let_stmt = make::let_stmt(bound_ident, None, Some(match_expr));
let let_stmt = let_stmt.indent(if_indent_level); let let_stmt = let_stmt.indent(if_indent_level);
replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
} }

View file

@ -85,11 +85,12 @@ pub(crate) fn inline_(
make::name("this"), make::name("this"),
) )
.into(), .into(),
None,
assoc_fn_params.next()?, assoc_fn_params.next()?,
)); ));
} }
for param in param_list.params() { for param in param_list.params() {
params.push((param.pat()?, assoc_fn_params.next()?)); params.push((param.pat()?, param.ty(), assoc_fn_params.next()?));
} }
if arg_list.len() != params.len() { if arg_list.len() != params.len() {
@ -123,7 +124,7 @@ pub(crate) fn inline_(
// has a pattern that does not allow inlining // has a pattern that does not allow inlining
let param_use_nodes: Vec<Vec<_>> = params let param_use_nodes: Vec<Vec<_>> = params
.iter() .iter()
.map(|(pat, param)| { .map(|(pat, _, param)| {
if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) { if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {
return Vec::new(); return Vec::new();
} }
@ -145,7 +146,7 @@ pub(crate) fn inline_(
// Rewrite `self` to `this` // Rewrite `self` to `this`
if param_list.self_param().is_some() { if param_list.self_param().is_some() {
let this = || make::name_ref("this").syntax().clone_for_update(); let this = || make::name_ref("this").syntax().clone_for_update();
usages_for_locals(params[0].1.as_local(ctx.sema.db)) usages_for_locals(params[0].2.as_local(ctx.sema.db))
.flat_map(|FileReference { name, range, .. }| match name { .flat_map(|FileReference { name, range, .. }| match name {
ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)), ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)),
_ => None, _ => None,
@ -156,7 +157,8 @@ pub(crate) fn inline_(
} }
// Inline parameter expressions or generate `let` statements depending on whether inlining works or not. // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
for ((pat, _), usages, expr) in izip!(params, param_use_nodes, arg_list).rev() { for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arg_list).rev()
{
let expr_is_name_ref = matches!(&expr, let expr_is_name_ref = matches!(&expr,
ast::Expr::PathExpr(expr) ast::Expr::PathExpr(expr)
if expr.path().and_then(|path| path.as_single_name_ref()).is_some() if expr.path().and_then(|path| path.as_single_name_ref()).is_some()
@ -184,8 +186,17 @@ pub(crate) fn inline_(
}); });
} }
// cant inline, emit a let statement // cant inline, emit a let statement
// FIXME: emit type ascriptions when a coercion happens? _ => {
_ => body.push_front(make::let_stmt(pat, Some(expr)).clone_for_update().into()), let ty = ctx
.sema
.type_of_expr_with_coercion(&expr)
.map_or(false, |(_, coerced)| coerced.is_some())
.then(|| param_ty)
.flatten();
body.push_front(
make::let_stmt(pat, ty, Some(expr)).clone_for_update().into(),
)
}
} }
} }
@ -606,6 +617,34 @@ fn foo(x: u32) -> u32{
fn main() { fn main() {
222; 222;
} }
"#,
);
}
#[test]
fn inline_emits_type_for_coercion() {
check_assist(
inline_call,
r#"
fn foo(x: *const u32) -> u32 {
x as u32
}
fn main() {
foo$0(&222);
}
"#,
r#"
fn foo(x: *const u32) -> u32 {
x as u32
}
fn main() {
{
let x: *const u32 = &222;
x as u32
};
}
"#, "#,
); );
} }

View file

@ -492,10 +492,19 @@ pub fn where_clause(preds: impl IntoIterator<Item = ast::WherePred>) -> ast::Whe
} }
} }
pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetStmt { pub fn let_stmt(
let text = match initializer { pattern: ast::Pat,
Some(it) => format!("let {} = {};", pattern, it), ty: Option<ast::Type>,
None => format!("let {};", pattern), initializer: Option<ast::Expr>,
) -> ast::LetStmt {
let mut text = String::new();
format_to!(text, "let {}", pattern);
if let Some(ty) = ty {
format_to!(text, ": {}", ty);
}
match initializer {
Some(it) => format_to!(text, " = {};", it),
None => format_to!(text, ";"),
}; };
ast_from_text(&format!("fn f() {{ {} }}", text)) ast_from_text(&format!("fn f() {{ {} }}", text))
} }