mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Merge #9557
9557: fix: Respect coercions in `inline_call` r=Veykril a=Veykril bors r+ Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
2e52d6ea93
6 changed files with 92 additions and 20 deletions
|
@ -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<Type> {
|
||||
pub fn type_of_expr_with_coercion(&self, expr: &ast::Expr) -> Option<(Type, Option<Type>)> {
|
||||
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<Type>)> {
|
||||
self.imp.type_of_pat_with_coercion(expr)
|
||||
}
|
||||
|
||||
pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
|
||||
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<Type> {
|
||||
fn type_of_expr_with_coercion(&self, expr: &ast::Expr) -> Option<(Type, Option<Type>)> {
|
||||
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<Type>)> {
|
||||
self.analyze(pat.syntax()).type_of_pat_with_coercion(self.db, pat)
|
||||
}
|
||||
|
||||
fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
|
||||
self.analyze(param.syntax()).type_of_self(self.db, param)
|
||||
}
|
||||
|
|
|
@ -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},
|
||||
|
@ -126,15 +126,15 @@ impl SourceAnalyzer {
|
|||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
expr: &ast::Expr,
|
||||
) -> Option<Type> {
|
||||
) -> Option<(Type, Option<Type>)> {
|
||||
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))
|
||||
.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: &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<Type> {
|
||||
|
@ -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<Type>)> {
|
||||
let pat_id = self.pat_id(pat)?;
|
||||
let infer = self.infer.as_ref()?;
|
||||
let coerced = infer
|
||||
.pat_adjustments
|
||||
.get(&pat_id)
|
||||
.and_then(|adjusts| adjusts.last().map(|adjust| &adjust.target));
|
||||
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(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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<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
|
||||
};
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
let text = match initializer {
|
||||
Some(it) => format!("let {} = {};", pattern, it),
|
||||
None => format!("let {};", pattern),
|
||||
pub fn let_stmt(
|
||||
pattern: ast::Pat,
|
||||
ty: Option<ast::Type>,
|
||||
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))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue