From 7e6f40b6f1339a16864e61a49f9d8f341184dc69 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Jul 2021 19:03:46 +0200 Subject: [PATCH 1/2] Expose coercions for patterns and expressions in semantics --- crates/hir/src/semantics.rs | 12 ++++++++-- crates/hir/src/source_analyzer.rs | 23 +++++++++++++++---- .../src/handlers/add_explicit_type.rs | 3 ++- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 984021d59e..a4028c523f 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -216,7 +216,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.type_of_expr(expr) } - pub fn type_of_expr_with_coercion(&self, expr: &ast::Expr) -> Option { + pub fn type_of_expr_with_coercion(&self, expr: &ast::Expr) -> Option<(Type, Option)> { self.imp.type_of_expr_with_coercion(expr) } @@ -224,6 +224,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.type_of_pat(pat) } + pub fn type_of_pat_with_coercion(&self, expr: &ast::Pat) -> Option<(Type, Option)> { + self.imp.type_of_pat_with_coercion(expr) + } + pub fn type_of_self(&self, param: &ast::SelfParam) -> Option { self.imp.type_of_self(param) } @@ -564,7 +568,7 @@ impl<'db> SemanticsImpl<'db> { self.analyze(expr.syntax()).type_of_expr(self.db, expr) } - fn type_of_expr_with_coercion(&self, expr: &ast::Expr) -> Option { + fn type_of_expr_with_coercion(&self, expr: &ast::Expr) -> Option<(Type, Option)> { self.analyze(expr.syntax()).type_of_expr_with_coercion(self.db, expr) } @@ -572,6 +576,10 @@ impl<'db> SemanticsImpl<'db> { self.analyze(pat.syntax()).type_of_pat(self.db, pat) } + fn type_of_pat_with_coercion(&self, pat: &ast::Pat)-> Option<(Type, Option)> { + self.analyze(pat.syntax()).type_of_pat_with_coercion(self.db, pat) + } + fn type_of_self(&self, param: &ast::SelfParam) -> Option { self.analyze(param.syntax()).type_of_self(self.db, param) } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 474fb3fe30..ff0b6697c7 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -126,15 +126,15 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, expr: &ast::Expr, - ) -> Option { + ) -> Option<(Type, Option)> { let expr_id = self.expr_id(db, expr)?; let infer = self.infer.as_ref()?; let ty = infer .expr_adjustments .get(&expr_id) - .and_then(|adjusts| adjusts.last().map(|adjust| &adjust.target)) - .unwrap_or_else(|| &infer[expr_id]); - Type::new_with_resolver(db, &self.resolver, ty.clone()) + .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()); + mk_ty(&infer[expr_id]).map(|it| (it, ty.and_then(mk_ty))) } pub(crate) fn type_of_pat(&self, db: &dyn HirDatabase, pat: &ast::Pat) -> Option { @@ -143,6 +143,21 @@ impl SourceAnalyzer { Type::new_with_resolver(db, &self.resolver, ty) } + pub(crate) fn type_of_pat_with_coercion( + &self, + db: &dyn HirDatabase, + pat: &ast::Pat, + ) -> Option<(Type, Option)> { + let pat_id = self.pat_id(pat)?; + let infer = self.infer.as_ref()?; + let ty = infer + .pat_adjustments + .get(&pat_id) + .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()); + mk_ty(&infer[pat_id]).map(|it| (it, ty.and_then(mk_ty))) + } + pub(crate) fn type_of_self( &self, db: &dyn HirDatabase, diff --git a/crates/ide_assists/src/handlers/add_explicit_type.rs b/crates/ide_assists/src/handlers/add_explicit_type.rs index b0e8fb6559..e545454159 100644 --- a/crates/ide_assists/src/handlers/add_explicit_type.rs +++ b/crates/ide_assists/src/handlers/add_explicit_type.rs @@ -55,7 +55,8 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio } // Infer type - let ty = ctx.sema.type_of_expr_with_coercion(&expr)?; + let (ty, coerced) = ctx.sema.type_of_expr_with_coercion(&expr)?; + let ty = coerced.unwrap_or(ty); if ty.contains_unknown() || ty.is_closure() { cov_mark::hit!(add_explicit_type_not_applicable_if_ty_not_inferred); return None; From 9e1eb77f6b65bca03b324687fcbb9abcdff4b2d6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Jul 2021 19:16:32 +0200 Subject: [PATCH 2/2] Respect coercions in `inline_call` --- crates/hir/src/semantics.rs | 2 +- crates/hir/src/source_analyzer.rs | 14 ++--- .../ide_assists/src/handlers/early_return.rs | 2 +- .../ide_assists/src/handlers/inline_call.rs | 51 ++++++++++++++++--- crates/syntax/src/ast/make.rs | 17 +++++-- 5 files changed, 67 insertions(+), 19 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index a4028c523f..cac4bce3f9 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -576,7 +576,7 @@ impl<'db> SemanticsImpl<'db> { self.analyze(pat.syntax()).type_of_pat(self.db, pat) } - fn type_of_pat_with_coercion(&self, pat: &ast::Pat)-> Option<(Type, Option)> { + fn type_of_pat_with_coercion(&self, pat: &ast::Pat) -> Option<(Type, Option)> { self.analyze(pat.syntax()).type_of_pat_with_coercion(self.db, pat) } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index ff0b6697c7..2ba8284fcb 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -21,7 +21,7 @@ use hir_def::{ use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; use hir_ty::{ diagnostics::{record_literal_missing_fields, record_pattern_missing_fields}, - InferenceResult, Interner, Substitution, TyExt, TyLoweringContext, + InferenceResult, Interner, Substitution, Ty, TyExt, TyLoweringContext, }; use syntax::{ ast::{self, AstNode}, @@ -129,12 +129,12 @@ impl SourceAnalyzer { ) -> Option<(Type, Option)> { let expr_id = self.expr_id(db, expr)?; let infer = self.infer.as_ref()?; - let ty = infer + let coerced = infer .expr_adjustments .get(&expr_id) .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()); - mk_ty(&infer[expr_id]).map(|it| (it, ty.and_then(mk_ty))) + let mk_ty = |ty: &Ty| Type::new_with_resolver(db, &self.resolver, ty.clone()); + 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 { @@ -150,12 +150,12 @@ impl SourceAnalyzer { ) -> Option<(Type, Option)> { let pat_id = self.pat_id(pat)?; let infer = self.infer.as_ref()?; - let ty = infer + let coerced = infer .pat_adjustments .get(&pat_id) .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()); - mk_ty(&infer[pat_id]).map(|it| (it, ty.and_then(mk_ty))) + let mk_ty = |ty: &Ty| Type::new_with_resolver(db, &self.resolver, ty.clone()); + mk_ty(&infer[pat_id]).map(|ty| (ty, coerced.and_then(mk_ty))) } pub(crate) fn type_of_self( diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs index a8e451e7ee..b4745b8424 100644 --- a/crates/ide_assists/src/handlers/early_return.rs +++ b/crates/ide_assists/src/handlers/early_return.rs @@ -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])) }; - 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); replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) } diff --git a/crates/ide_assists/src/handlers/inline_call.rs b/crates/ide_assists/src/handlers/inline_call.rs index 5bd81ba051..0dcc12d8cb 100644 --- a/crates/ide_assists/src/handlers/inline_call.rs +++ b/crates/ide_assists/src/handlers/inline_call.rs @@ -85,11 +85,12 @@ pub(crate) fn inline_( make::name("this"), ) .into(), + None, assoc_fn_params.next()?, )); } 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() { @@ -123,7 +124,7 @@ pub(crate) fn inline_( // has a pattern that does not allow inlining let param_use_nodes: Vec> = params .iter() - .map(|(pat, param)| { + .map(|(pat, _, param)| { if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) { return Vec::new(); } @@ -145,7 +146,7 @@ pub(crate) fn inline_( // Rewrite `self` to `this` if param_list.self_param().is_some() { 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 { ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)), _ => None, @@ -156,7 +157,8 @@ pub(crate) fn inline_( } // 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, ast::Expr::PathExpr(expr) 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 - // 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() { 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 + }; +} "#, ); } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 1eaf40f4cb..f84e175538 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -492,10 +492,19 @@ pub fn where_clause(preds: impl IntoIterator) -> ast::Whe } } -pub fn let_stmt(pattern: ast::Pat, initializer: Option) -> ast::LetStmt { - let text = match initializer { - Some(it) => format!("let {} = {};", pattern, it), - None => format!("let {};", pattern), +pub fn let_stmt( + pattern: ast::Pat, + ty: Option, + initializer: Option, +) -> 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)) }