Add flag to disallow opaque types for DisplayTarget::SourceCode

This commit is contained in:
Ryo Yoshida 2023-04-07 22:45:04 +09:00
parent 7501d3b721
commit ac03de773f
No known key found for this signature in database
GPG key ID: E25698A930586171
15 changed files with 62 additions and 29 deletions

View file

@ -150,6 +150,7 @@ pub trait HirDisplay {
&'a self, &'a self,
db: &'a dyn HirDatabase, db: &'a dyn HirDatabase,
module_id: ModuleId, module_id: ModuleId,
allow_opaque: bool,
) -> Result<String, DisplaySourceCodeError> { ) -> Result<String, DisplaySourceCodeError> {
let mut result = String::new(); let mut result = String::new();
match self.hir_fmt(&mut HirFormatter { match self.hir_fmt(&mut HirFormatter {
@ -160,7 +161,7 @@ pub trait HirDisplay {
max_size: None, max_size: None,
omit_verbose_types: false, omit_verbose_types: false,
closure_style: ClosureStyle::ImplFn, closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::SourceCode { module_id }, display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
}) { }) {
Ok(()) => {} Ok(()) => {}
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@ -249,18 +250,26 @@ pub enum DisplayTarget {
Diagnostics, Diagnostics,
/// Display types for inserting them in source files. /// Display types for inserting them in source files.
/// The generated code should compile, so paths need to be qualified. /// The generated code should compile, so paths need to be qualified.
SourceCode { module_id: ModuleId }, SourceCode { module_id: ModuleId, allow_opaque: bool },
/// Only for test purpose to keep real types /// Only for test purpose to keep real types
Test, Test,
} }
impl DisplayTarget { impl DisplayTarget {
fn is_source_code(&self) -> bool { fn is_source_code(self) -> bool {
matches!(self, Self::SourceCode { .. }) matches!(self, Self::SourceCode { .. })
} }
fn is_test(&self) -> bool {
fn is_test(self) -> bool {
matches!(self, Self::Test) matches!(self, Self::Test)
} }
fn allows_opaque(self) -> bool {
match self {
Self::SourceCode { allow_opaque, .. } => allow_opaque,
_ => true,
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -268,6 +277,7 @@ pub enum DisplaySourceCodeError {
PathNotFound, PathNotFound,
UnknownType, UnknownType,
Generator, Generator,
OpaqueType,
} }
pub enum HirDisplayError { pub enum HirDisplayError {
@ -768,7 +778,7 @@ impl HirDisplay for Ty {
}; };
write!(f, "{name}")?; write!(f, "{name}")?;
} }
DisplayTarget::SourceCode { module_id } => { DisplayTarget::SourceCode { module_id, allow_opaque: _ } => {
if let Some(path) = find_path::find_path( if let Some(path) = find_path::find_path(
db.upcast(), db.upcast(),
ItemInNs::Types((*def_id).into()), ItemInNs::Types((*def_id).into()),
@ -906,6 +916,11 @@ impl HirDisplay for Ty {
f.end_location_link(); f.end_location_link();
} }
TyKind::OpaqueType(opaque_ty_id, parameters) => { TyKind::OpaqueType(opaque_ty_id, parameters) => {
if !f.display_target.allows_opaque() {
return Err(HirDisplayError::DisplaySourceCodeError(
DisplaySourceCodeError::OpaqueType,
));
}
let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
match impl_trait_id { match impl_trait_id {
ImplTraitId::ReturnTypeImplTrait(func, idx) => { ImplTraitId::ReturnTypeImplTrait(func, idx) => {
@ -953,9 +968,15 @@ impl HirDisplay for Ty {
} }
} }
TyKind::Closure(id, substs) => { TyKind::Closure(id, substs) => {
if f.display_target.is_source_code() && f.closure_style != ClosureStyle::ImplFn { if f.display_target.is_source_code() {
if !f.display_target.allows_opaque() {
return Err(HirDisplayError::DisplaySourceCodeError(
DisplaySourceCodeError::OpaqueType,
));
} else if f.closure_style != ClosureStyle::ImplFn {
never!("Only `impl Fn` is valid for displaying closures in source code"); never!("Only `impl Fn` is valid for displaying closures in source code");
} }
}
match f.closure_style { match f.closure_style {
ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"), ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
ClosureStyle::ClosureWithId => { ClosureStyle::ClosureWithId => {
@ -1053,6 +1074,11 @@ impl HirDisplay for Ty {
} }
TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?, TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
if !f.display_target.allows_opaque() {
return Err(HirDisplayError::DisplaySourceCodeError(
DisplaySourceCodeError::OpaqueType,
));
}
let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into()); let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into());
match impl_trait_id { match impl_trait_id {
ImplTraitId::ReturnTypeImplTrait(func, idx) => { ImplTraitId::ReturnTypeImplTrait(func, idx) => {

View file

@ -159,7 +159,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let range = node.as_ref().original_file_range(&db); let range = node.as_ref().original_file_range(&db);
if let Some(expected) = types.remove(&range) { if let Some(expected) = types.remove(&range) {
let actual = if display_source { let actual = if display_source {
ty.display_source_code(&db, def.module(&db)).unwrap() ty.display_source_code(&db, def.module(&db), true).unwrap()
} else { } else {
ty.display_test(&db).to_string() ty.display_test(&db).to_string()
}; };
@ -175,7 +175,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let range = node.as_ref().original_file_range(&db); let range = node.as_ref().original_file_range(&db);
if let Some(expected) = types.remove(&range) { if let Some(expected) = types.remove(&range) {
let actual = if display_source { let actual = if display_source {
ty.display_source_code(&db, def.module(&db)).unwrap() ty.display_source_code(&db, def.module(&db), true).unwrap()
} else { } else {
ty.display_test(&db).to_string() ty.display_test(&db).to_string()
}; };

View file

@ -69,7 +69,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
return None; return None;
} }
let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?; let inferred_type = ty.display_source_code(ctx.db(), module.into(), false).ok()?;
acc.add( acc.add(
AssistId("add_explicit_type", AssistKind::RefactorRewrite), AssistId("add_explicit_type", AssistKind::RefactorRewrite),
format!("Insert explicit type `{inferred_type}`"), format!("Insert explicit type `{inferred_type}`"),

View file

@ -22,7 +22,7 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
if ty.is_unit() { if ty.is_unit() {
return None; return None;
} }
let ty = ty.display_source_code(ctx.db(), module.into()).ok()?; let ty = ty.display_source_code(ctx.db(), module.into(), true).ok()?;
acc.add( acc.add(
AssistId("add_return_type", AssistKind::RefactorRewrite), AssistId("add_return_type", AssistKind::RefactorRewrite),

View file

@ -1884,7 +1884,7 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr
} }
fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String { fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String {
ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "_".to_string()) ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_string())
} }
fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type { fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {

View file

@ -46,7 +46,8 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
let ty = ctx.sema.type_of_expr(&expr)?; let ty = ctx.sema.type_of_expr(&expr)?;
let scope = ctx.sema.scope(statement.syntax())?; let scope = ctx.sema.scope(statement.syntax())?;
let constant_module = scope.module(); let constant_module = scope.module();
let type_name = ty.original().display_source_code(ctx.db(), constant_module.into()).ok()?; let type_name =
ty.original().display_source_code(ctx.db(), constant_module.into(), false).ok()?;
let target = statement.syntax().parent()?.text_range(); let target = statement.syntax().parent()?.text_range();
let path = constant_token.syntax().ancestors().find_map(ast::Path::cast)?; let path = constant_token.syntax().ancestors().find_map(ast::Path::cast)?;

View file

@ -192,7 +192,7 @@ fn expr_ty(
scope: &hir::SemanticsScope<'_>, scope: &hir::SemanticsScope<'_>,
) -> Option<ast::Type> { ) -> Option<ast::Type> {
let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted())?; let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted())?;
let text = ty.display_source_code(ctx.db(), scope.module().into()).ok()?; let text = ty.display_source_code(ctx.db(), scope.module().into(), false).ok()?;
Some(make::ty(&text)) Some(make::ty(&text))
} }

View file

@ -438,7 +438,7 @@ fn make_return_type(
Some(ty) if ty.is_unit() => (None, false), Some(ty) if ty.is_unit() => (None, false),
Some(ty) => { Some(ty) => {
necessary_generic_params.extend(ty.generic_params(ctx.db())); necessary_generic_params.extend(ty.generic_params(ctx.db()));
let rendered = ty.display_source_code(ctx.db(), target_module.into()); let rendered = ty.display_source_code(ctx.db(), target_module.into(), true);
match rendered { match rendered {
Ok(rendered) => (Some(make::ty(&rendered)), false), Ok(rendered) => (Some(make::ty(&rendered)), false),
Err(_) => (Some(make::ty_placeholder()), true), Err(_) => (Some(make::ty_placeholder()), true),
@ -992,9 +992,9 @@ fn fn_arg_type(
let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate()); let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
convert_reference_type(ty.strip_references(), ctx.db(), famous_defs) convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
.map(|conversion| conversion.convert_type(ctx.db())) .map(|conversion| conversion.convert_type(ctx.db()))
.or_else(|| ty.display_source_code(ctx.db(), target_module.into()).ok()) .or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
} else { } else {
ty.display_source_code(ctx.db(), target_module.into()).ok() ty.display_source_code(ctx.db(), target_module.into(), true).ok()
} }
} }

View file

@ -57,11 +57,13 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
let local = ctx.sema.to_def(&pat)?; let local = ctx.sema.to_def(&pat)?;
let ty = ctx.sema.type_of_pat(&pat.into())?.original; let ty = ctx.sema.type_of_pat(&pat.into())?.original;
if ty.contains_unknown() || ty.is_closure() { let ty = match ty.display_source_code(ctx.db(), module.into(), false) {
cov_mark::hit!(promote_lcoal_not_applicable_if_ty_not_inferred); Ok(ty) => ty,
Err(_) => {
cov_mark::hit!(promote_local_not_applicable_if_ty_not_inferred);
return None; return None;
} }
let ty = ty.display_source_code(ctx.db(), module.into()).ok()?; };
let initializer = let_stmt.initializer()?; let initializer = let_stmt.initializer()?;
if !is_body_const(&ctx.sema, &initializer) { if !is_body_const(&ctx.sema, &initializer) {
@ -187,7 +189,7 @@ fn foo() {
#[test] #[test]
fn not_applicable_unknown_ty() { fn not_applicable_unknown_ty() {
cov_mark::check!(promote_lcoal_not_applicable_if_ty_not_inferred); cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred);
check_assist_not_applicable( check_assist_not_applicable(
promote_local_to_const, promote_local_to_const,
r" r"

View file

@ -55,7 +55,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
let returned_type = match ctx.sema.type_of_expr(&initializer) { let returned_type = match ctx.sema.type_of_expr(&initializer) {
Some(returned_type) if !returned_type.original.contains_unknown() => { Some(returned_type) if !returned_type.original.contains_unknown() => {
let module = ctx.sema.scope(let_stmt.syntax())?.module(); let module = ctx.sema.scope(let_stmt.syntax())?.module();
returned_type.original.display_source_code(ctx.db(), module.into()).ok()? returned_type.original.display_source_code(ctx.db(), module.into(), false).ok()?
} }
_ => { _ => {
cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available); cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);

View file

@ -127,7 +127,7 @@ fn params_from_stmt_list_scope(
let module = scope.module().into(); let module = scope.module().into();
scope.process_all_names(&mut |name, def| { scope.process_all_names(&mut |name, def| {
if let hir::ScopeDef::Local(local) = def { if let hir::ScopeDef::Local(local) = def {
if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module) { if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module, true) {
cb(name, ty); cb(name, ty);
} }
} }

View file

@ -242,7 +242,7 @@ pub(crate) fn complete_ascribed_type(
} }
}? }?
.adjusted(); .adjusted();
let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?; let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
acc.add(render_type_inference(ty_string, ctx)); acc.add(render_type_inference(ty_string, ctx));
None None
} }

View file

@ -116,7 +116,9 @@ impl<'a> PathTransform<'a> {
Some(( Some((
k, k,
ast::make::ty( ast::make::ty(
&default.display_source_code(db, source_module.into()).ok()?, &default
.display_source_code(db, source_module.into(), false)
.ok()?,
), ),
)) ))
} }

View file

@ -176,7 +176,9 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
fn make_ty(ty: &hir::Type, db: &dyn HirDatabase, module: hir::Module) -> ast::Type { fn make_ty(ty: &hir::Type, db: &dyn HirDatabase, module: hir::Module) -> ast::Type {
let ty_str = match ty.as_adt() { let ty_str = match ty.as_adt() {
Some(adt) => adt.name(db).to_string(), Some(adt) => adt.name(db).to_string(),
None => ty.display_source_code(db, module.into()).ok().unwrap_or_else(|| "_".to_string()), None => {
ty.display_source_code(db, module.into(), false).ok().unwrap_or_else(|| "_".to_string())
}
}; };
make::ty(&ty_str) make::ty(&ty_str)

View file

@ -69,7 +69,7 @@ fn missing_record_expr_field_fixes(
let new_field = make::record_field( let new_field = make::record_field(
None, None,
make::name(record_expr_field.field_name()?.ident_token()?.text()), make::name(record_expr_field.field_name()?.ident_token()?.text()),
make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), make::ty(&new_field_type.display_source_code(sema.db, module.into(), true).ok()?),
); );
let last_field = record_fields.fields().last()?; let last_field = record_fields.fields().last()?;