Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide_assists/src/utils.rs')
-rw-r--r--crates/ide_assists/src/utils.rs168
1 files changed, 146 insertions, 22 deletions
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 6866186d34..fd0ff2f5cb 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -1,13 +1,14 @@
//! Assorted functions shared by several assists.
-pub(crate) mod suggest_name;
-mod gen_trait_fn_body;
-
use std::ops;
-use hir::HasSource;
-use ide_db::{helpers::SnippetCap, path_transform::PathTransform, RootDatabase};
use itertools::Itertools;
+
+pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
+use hir::{db::HirDatabase, HasSource, HirDisplay};
+use ide_db::{
+ helpers::FamousDefs, helpers::SnippetCap, path_transform::PathTransform, RootDatabase,
+};
use stdx::format_to;
use syntax::{
ast::{
@@ -23,7 +24,8 @@ use syntax::{
use crate::assist_context::{AssistBuilder, AssistContext};
-pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
+pub(crate) mod suggest_name;
+mod gen_trait_fn_body;
pub(crate) fn unwrap_trivial_block(block_expr: ast::BlockExpr) -> ast::Expr {
extract_trivial_expression(&block_expr)
@@ -507,27 +509,149 @@ pub(crate) fn add_method_to_adt(
builder.insert(start_offset, buf);
}
-pub fn useless_type_special_case(field_name: &str, field_ty: &String) -> Option<(String, String)> {
- if field_ty == "String" {
- cov_mark::hit!(useless_type_special_case);
- return Some(("&str".to_string(), format!("self.{}.as_str()", field_name)));
- }
- if let Some(arg) = ty_ctor(field_ty, "Vec") {
- return Some((format!("&[{}]", arg), format!("self.{}.as_slice()", field_name)));
+#[derive(Debug)]
+pub(crate) struct ReferenceConversion {
+ conversion: ReferenceConversionType,
+ ty: hir::Type,
+}
+
+#[derive(Debug)]
+enum ReferenceConversionType {
+ // reference can be stripped if the type is Copy
+ Copy,
+ // &String -> &str
+ AsRefStr,
+ // &Vec<T> -> &[T]
+ AsRefSlice,
+ // &Box<T> -> &T
+ Dereferenced,
+ // &Option<T> -> Option<&T>
+ Option,
+ // &Result<T, E> -> Result<&T, &E>
+ Result,
+}
+
+impl ReferenceConversion {
+ pub(crate) fn convert_type(&self, db: &dyn HirDatabase) -> String {
+ match self.conversion {
+ ReferenceConversionType::Copy => self.ty.display(db).to_string(),
+ ReferenceConversionType::AsRefStr => "&str".to_string(),
+ ReferenceConversionType::AsRefSlice => {
+ let type_argument_name =
+ self.ty.type_arguments().next().unwrap().display(db).to_string();
+ format!("&[{}]", type_argument_name)
+ }
+ ReferenceConversionType::Dereferenced => {
+ let type_argument_name =
+ self.ty.type_arguments().next().unwrap().display(db).to_string();
+ format!("&{}", type_argument_name)
+ }
+ ReferenceConversionType::Option => {
+ let type_argument_name =
+ self.ty.type_arguments().next().unwrap().display(db).to_string();
+ format!("Option<&{}>", type_argument_name)
+ }
+ ReferenceConversionType::Result => {
+ let mut type_arguments = self.ty.type_arguments();
+ let first_type_argument_name =
+ type_arguments.next().unwrap().display(db).to_string();
+ let second_type_argument_name =
+ type_arguments.next().unwrap().display(db).to_string();
+ format!("Result<&{}, &{}>", first_type_argument_name, second_type_argument_name)
+ }
+ }
}
- if let Some(arg) = ty_ctor(field_ty, "Box") {
- return Some((format!("&{}", arg), format!("self.{}.as_ref()", field_name)));
+
+ pub(crate) fn getter(&self, field_name: String) -> String {
+ match self.conversion {
+ ReferenceConversionType::Copy => format!("self.{}", field_name),
+ ReferenceConversionType::AsRefStr
+ | ReferenceConversionType::AsRefSlice
+ | ReferenceConversionType::Dereferenced
+ | ReferenceConversionType::Option
+ | ReferenceConversionType::Result => format!("self.{}.as_ref()", field_name),
+ }
}
- if let Some(arg) = ty_ctor(field_ty, "Option") {
- return Some((format!("Option<&{}>", arg), format!("self.{}.as_ref()", field_name)));
+}
+
+// FIXME: It should return a new hir::Type, but currently constructing new types is too cumbersome
+// and all users of this function operate on string type names, so they can do the conversion
+// itself themselves.
+pub(crate) fn convert_reference_type(
+ ty: hir::Type,
+ db: &RootDatabase,
+ famous_defs: &FamousDefs,
+) -> Option<ReferenceConversion> {
+ handle_copy(&ty, db)
+ .or_else(|| handle_as_ref_str(&ty, db, famous_defs))
+ .or_else(|| handle_as_ref_slice(&ty, db, famous_defs))
+ .or_else(|| handle_dereferenced(&ty, db, famous_defs))
+ .or_else(|| handle_option_as_ref(&ty, db, famous_defs))
+ .or_else(|| handle_result_as_ref(&ty, db, famous_defs))
+ .map(|conversion| ReferenceConversion { ty, conversion })
+}
+
+fn handle_copy(ty: &hir::Type, db: &dyn HirDatabase) -> Option<ReferenceConversionType> {
+ ty.is_copy(db).then(|| ReferenceConversionType::Copy)
+}
+
+fn handle_as_ref_str(
+ ty: &hir::Type,
+ db: &dyn HirDatabase,
+ famous_defs: &FamousDefs,
+) -> Option<ReferenceConversionType> {
+ let module = famous_defs.1?.root_module(db);
+ let str_type = hir::BuiltinType::str().ty(db, module);
+
+ ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[str_type])
+ .then(|| ReferenceConversionType::AsRefStr)
+}
+
+fn handle_as_ref_slice(
+ ty: &hir::Type,
+ db: &dyn HirDatabase,
+ famous_defs: &FamousDefs,
+) -> Option<ReferenceConversionType> {
+ let type_argument = ty.type_arguments().next()?;
+ let slice_type = hir::Type::new_slice(type_argument);
+
+ ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[slice_type])
+ .then(|| ReferenceConversionType::AsRefSlice)
+}
+
+fn handle_dereferenced(
+ ty: &hir::Type,
+ db: &dyn HirDatabase,
+ famous_defs: &FamousDefs,
+) -> Option<ReferenceConversionType> {
+ let type_argument = ty.type_arguments().next()?;
+
+ ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[type_argument])
+ .then(|| ReferenceConversionType::Dereferenced)
+}
+
+fn handle_option_as_ref(
+ ty: &hir::Type,
+ db: &dyn HirDatabase,
+ famous_defs: &FamousDefs,
+) -> Option<ReferenceConversionType> {
+ if ty.as_adt() == famous_defs.core_option_Option()?.ty(db).as_adt() {
+ Some(ReferenceConversionType::Option)
+ } else {
+ None
}
- None
}
-// FIXME: This should rely on semantic info.
-fn ty_ctor(ty: &String, ctor: &str) -> Option<String> {
- let res = ty.to_string().strip_prefix(ctor)?.strip_prefix('<')?.strip_suffix('>')?.to_string();
- Some(res)
+fn handle_result_as_ref(
+ ty: &hir::Type,
+ db: &dyn HirDatabase,
+ famous_defs: &FamousDefs,
+) -> Option<ReferenceConversionType> {
+ if ty.as_adt() == famous_defs.core_result_Result()?.ty(db).as_adt() {
+ Some(ReferenceConversionType::Result)
+ } else {
+ None
+ }
}
pub(crate) fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {