+use ide_db::helpers::FamousDefs;
use stdx::{format_to, to_lower_snake_case};
-use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
+use syntax::ast::{self, AstNode, HasName, HasVisibility};
use crate::{
- utils::useless_type_special_case,
- utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
+ utils::{convert_reference_type, find_impl_block_end, find_struct_impl, generate_impl_text},
AssistContext, AssistId, AssistKind, Assists, GroupLabel,
};
// Generate a getter method.
//
// ```
+// # //- minicore: as_ref
+// # pub struct String;
+// # impl AsRef<str> for String {
+// # fn as_ref(&self) -> &str {
+// # ""
+// # }
+// # }
+// #
// struct Person {
// nam$0e: String,
// }
// ```
// ->
// ```
+// # pub struct String;
+// # impl AsRef<str> for String {
+// # fn as_ref(&self) -> &str {
+// # ""
+// # }
+// # }
+// #
// struct Person {
// name: String,
// }
// impl Person {
// /// Get a reference to the person's name.
// fn $0name(&self) -> &str {
-// self.name.as_str()
+// self.name.as_ref()
// }
// }
// ```
}
let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
- let (ty, body) = if mutable {
- (format!("&mut {}", field_ty), format!("&mut self.{}", field_name))
+ let (ty, body, description) = if mutable {
+ (
+ format!("&mut {}", field_ty),
+ format!("&mut self.{}", field_name),
+ "a mutable reference to ",
+ )
} else {
- useless_type_special_case(&field_name.to_string(), &field_ty.to_string())
- .unwrap_or_else(|| (format!("&{}", field_ty), format!("&self.{}", field_name)))
+ let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(field_ty.syntax()).krate());
+ ctx.sema
+ .resolve_type(&field_ty)
+ .and_then(|ty| convert_reference_type(ty, ctx.db(), famous_defs))
+ .map(|conversion| {
+ cov_mark::hit!(convert_reference_type);
+ (
+ conversion.convert_type(ctx.db()),
+ conversion.getter(field_name.to_string()),
+ if conversion.is_copy() { "" } else { "a reference to " },
+ )
+ })
+ .unwrap_or_else(|| {
+ (
+ format!("&{}", field_ty),
+ format!("&self.{}", field_name),
+ "a reference to ",
+ )
+ })
};
format_to!(
buf,
- " /// Get a {}reference to the {}'s {}.
+ " /// Get {}the {}'s {}.
{}fn {}(&{}self) -> {} {{
{}
}}",
- mutable.then(|| "mutable ").unwrap_or_default(),
+ description,
to_lower_snake_case(&strukt_name.to_string()).replace('_', " "),
fn_name.trim_end_matches("_mut").replace('_', " "),
vis,
}
#[test]
- fn test_special_cases() {
- cov_mark::check!(useless_type_special_case);
+ fn test_not_a_special_case() {
+ cov_mark::check_count!(convert_reference_type, 0);
+ // Fake string which doesn't implement AsRef<str>
check_assist(
generate_getter,
r#"
+pub struct String;
+
+struct S { foo: $0String }
+"#,
+ r#"
+pub struct String;
+
+struct S { foo: String }
+
+impl S {
+ /// Get a reference to the s's foo.
+ fn $0foo(&self) -> &String {
+ &self.foo
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_convert_reference_type() {
+ cov_mark::check_count!(convert_reference_type, 6);
+
+ // Copy
+ check_assist(
+ generate_getter,
+ r#"
+//- minicore: copy
+struct S { foo: $0bool }
+"#,
+ r#"
+struct S { foo: bool }
+
+impl S {
+ /// Get the s's foo.
+ fn $0foo(&self) -> bool {
+ self.foo
+ }
+}
+"#,
+ );
+
+ // AsRef<str>
+ check_assist(
+ generate_getter,
+ r#"
+//- minicore: as_ref
+pub struct String;
+impl AsRef<str> for String {
+ fn as_ref(&self) -> &str {
+ ""
+ }
+}
+
struct S { foo: $0String }
"#,
r#"
+pub struct String;
+impl AsRef<str> for String {
+ fn as_ref(&self) -> &str {
+ ""
+ }
+}
+
struct S { foo: String }
impl S {
/// Get a reference to the s's foo.
fn $0foo(&self) -> &str {
- self.foo.as_str()
+ self.foo.as_ref()
}
}
"#,
);
+
+ // AsRef<T>
check_assist(
generate_getter,
r#"
+//- minicore: as_ref
+struct Sweets;
+
+pub struct Box<T>(T);
+impl<T> AsRef<T> for Box<T> {
+ fn as_ref(&self) -> &T {
+ &self.0
+ }
+}
+
struct S { foo: $0Box<Sweets> }
"#,
r#"
+struct Sweets;
+
+pub struct Box<T>(T);
+impl<T> AsRef<T> for Box<T> {
+ fn as_ref(&self) -> &T {
+ &self.0
+ }
+}
+
struct S { foo: Box<Sweets> }
impl S {
}
"#,
);
+
+ // AsRef<[T]>
check_assist(
generate_getter,
r#"
+//- minicore: as_ref
+pub struct Vec<T>;
+impl<T> AsRef<[T]> for Vec<T> {
+ fn as_ref(&self) -> &[T] {
+ &[]
+ }
+}
+
struct S { foo: $0Vec<()> }
"#,
r#"
+pub struct Vec<T>;
+impl<T> AsRef<[T]> for Vec<T> {
+ fn as_ref(&self) -> &[T] {
+ &[]
+ }
+}
+
struct S { foo: Vec<()> }
impl S {
/// Get a reference to the s's foo.
fn $0foo(&self) -> &[()] {
- self.foo.as_slice()
+ self.foo.as_ref()
}
}
"#,
);
+
+ // Option
check_assist(
generate_getter,
r#"
+//- minicore: option
+struct Failure;
+
struct S { foo: $0Option<Failure> }
"#,
r#"
+struct Failure;
+
struct S { foo: Option<Failure> }
impl S {
self.foo.as_ref()
}
}
+"#,
+ );
+
+ // Result
+ check_assist(
+ generate_getter,
+ r#"
+//- minicore: result
+struct Context {
+ dat$0a: Result<bool, i32>,
+}
+"#,
+ r#"
+struct Context {
+ data: Result<bool, i32>,
+}
+
+impl Context {
+ /// Get a reference to the context's data.
+ fn $0data(&self) -> Result<&bool, &i32> {
+ self.data.as_ref()
+ }
+}
"#,
);
}