aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/handlers/generate_default_from_enum_variant.rs175
-rw-r--r--crates/assists/src/handlers/invert_if.rs9
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs27
-rw-r--r--crates/assists/src/utils.rs4
-rw-r--r--crates/hir/src/has_source.rs4
-rw-r--r--crates/hir_def/src/adt.rs13
-rw-r--r--crates/hir_def/src/generics.rs38
-rw-r--r--crates/hir_def/src/src.rs5
-rw-r--r--crates/syntax/src/ast/make.rs3
-rw-r--r--docs/dev/README.md14
11 files changed, 271 insertions, 23 deletions
diff --git a/crates/assists/src/handlers/generate_default_from_enum_variant.rs b/crates/assists/src/handlers/generate_default_from_enum_variant.rs
new file mode 100644
index 000000000..bcea46735
--- /dev/null
+++ b/crates/assists/src/handlers/generate_default_from_enum_variant.rs
@@ -0,0 +1,175 @@
1use ide_db::helpers::FamousDefs;
2use ide_db::RootDatabase;
3use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark;
5
6use crate::{AssistContext, AssistId, AssistKind, Assists};
7
8// Assist: generate_default_from_enum_variant
9//
10// Adds a Default impl for an enum using a variant.
11//
12// ```
13// enum Version {
14// Undefined,
15// Minor<|>,
16// Major,
17// }
18// ```
19// ->
20// ```
21// enum Version {
22// Undefined,
23// Minor,
24// Major,
25// }
26//
27// impl Default for Version {
28// fn default() -> Self {
29// Self::Minor
30// }
31// }
32// ```
33pub(crate) fn generate_default_from_enum_variant(
34 acc: &mut Assists,
35 ctx: &AssistContext,
36) -> Option<()> {
37 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
38 let variant_name = variant.name()?;
39 let enum_name = variant.parent_enum().name()?;
40 if !matches!(variant.kind(), ast::StructKind::Unit) {
41 mark::hit!(test_gen_default_on_non_unit_variant_not_implemented);
42 return None;
43 }
44
45 if existing_default_impl(&ctx.sema, &variant).is_some() {
46 mark::hit!(test_gen_default_impl_already_exists);
47 return None;
48 }
49
50 let target = variant.syntax().text_range();
51 acc.add(
52 AssistId("generate_default_from_enum_variant", AssistKind::Generate),
53 "Generate `Default` impl from this enum variant",
54 target,
55 |edit| {
56 let start_offset = variant.parent_enum().syntax().text_range().end();
57 let buf = format!(
58 r#"
59
60impl Default for {0} {{
61 fn default() -> Self {{
62 Self::{1}
63 }}
64}}"#,
65 enum_name, variant_name
66 );
67 edit.insert(start_offset, buf);
68 },
69 )
70}
71
72fn existing_default_impl(
73 sema: &'_ hir::Semantics<'_, RootDatabase>,
74 variant: &ast::Variant,
75) -> Option<()> {
76 let variant = sema.to_def(variant)?;
77 let enum_ = variant.parent_enum(sema.db);
78 let krate = enum_.module(sema.db).krate();
79
80 let default_trait = FamousDefs(sema, Some(krate)).core_default_Default()?;
81 let enum_type = enum_.ty(sema.db);
82
83 if enum_type.impls_trait(sema.db, default_trait, &[]) {
84 Some(())
85 } else {
86 None
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use test_utils::mark;
93
94 use crate::tests::{check_assist, check_assist_not_applicable};
95
96 use super::*;
97
98 fn check_not_applicable(ra_fixture: &str) {
99 let fixture =
100 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
101 check_assist_not_applicable(generate_default_from_enum_variant, &fixture)
102 }
103
104 #[test]
105 fn test_generate_default_from_variant() {
106 check_assist(
107 generate_default_from_enum_variant,
108 r#"
109enum Variant {
110 Undefined,
111 Minor<|>,
112 Major,
113}"#,
114 r#"enum Variant {
115 Undefined,
116 Minor,
117 Major,
118}
119
120impl Default for Variant {
121 fn default() -> Self {
122 Self::Minor
123 }
124}"#,
125 );
126 }
127
128 #[test]
129 fn test_generate_default_already_implemented() {
130 mark::check!(test_gen_default_impl_already_exists);
131 check_not_applicable(
132 r#"
133enum Variant {
134 Undefined,
135 Minor<|>,
136 Major,
137}
138
139impl Default for Variant {
140 fn default() -> Self {
141 Self::Minor
142 }
143}"#,
144 );
145 }
146
147 #[test]
148 fn test_add_from_impl_no_element() {
149 mark::check!(test_gen_default_on_non_unit_variant_not_implemented);
150 check_not_applicable(
151 r#"
152enum Variant {
153 Undefined,
154 Minor(u32)<|>,
155 Major,
156}"#,
157 );
158 }
159
160 #[test]
161 fn test_generate_default_from_variant_with_one_variant() {
162 check_assist(
163 generate_default_from_enum_variant,
164 r#"enum Variant { Undefi<|>ned }"#,
165 r#"
166enum Variant { Undefined }
167
168impl Default for Variant {
169 fn default() -> Self {
170 Self::Undefined
171 }
172}"#,
173 );
174 }
175}
diff --git a/crates/assists/src/handlers/invert_if.rs b/crates/assists/src/handlers/invert_if.rs
index ea722b91b..91e2f5c8c 100644
--- a/crates/assists/src/handlers/invert_if.rs
+++ b/crates/assists/src/handlers/invert_if.rs
@@ -69,6 +69,15 @@ mod tests {
69 use crate::tests::{check_assist, check_assist_not_applicable}; 69 use crate::tests::{check_assist, check_assist_not_applicable};
70 70
71 #[test] 71 #[test]
72 fn invert_if_composite_condition() {
73 check_assist(
74 invert_if,
75 "fn f() { i<|>f x == 3 || x == 4 || x == 5 { 1 } else { 3 * 2 } }",
76 "fn f() { if !(x == 3 || x == 4 || x == 5) { 3 * 2 } else { 1 } }",
77 )
78 }
79
80 #[test]
72 fn invert_if_remove_inequality() { 81 fn invert_if_remove_inequality() {
73 check_assist( 82 check_assist(
74 invert_if, 83 invert_if,
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index b8ce7418d..6e736ccb3 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -137,6 +137,7 @@ mod handlers {
137 mod flip_comma; 137 mod flip_comma;
138 mod flip_trait_bound; 138 mod flip_trait_bound;
139 mod generate_derive; 139 mod generate_derive;
140 mod generate_default_from_enum_variant;
140 mod generate_from_impl_for_enum; 141 mod generate_from_impl_for_enum;
141 mod generate_function; 142 mod generate_function;
142 mod generate_impl; 143 mod generate_impl;
@@ -186,6 +187,7 @@ mod handlers {
186 flip_comma::flip_comma, 187 flip_comma::flip_comma,
187 flip_trait_bound::flip_trait_bound, 188 flip_trait_bound::flip_trait_bound,
188 generate_derive::generate_derive, 189 generate_derive::generate_derive,
190 generate_default_from_enum_variant::generate_default_from_enum_variant,
189 generate_from_impl_for_enum::generate_from_impl_for_enum, 191 generate_from_impl_for_enum::generate_from_impl_for_enum,
190 generate_function::generate_function, 192 generate_function::generate_function,
191 generate_impl::generate_impl, 193 generate_impl::generate_impl,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 853bde09c..cc7c4a343 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -365,6 +365,33 @@ fn foo<T: Copy + Clone>() { }
365} 365}
366 366
367#[test] 367#[test]
368fn doctest_generate_default_from_enum_variant() {
369 check_doc_test(
370 "generate_default_from_enum_variant",
371 r#####"
372enum Version {
373 Undefined,
374 Minor<|>,
375 Major,
376}
377"#####,
378 r#####"
379enum Version {
380 Undefined,
381 Minor,
382 Major,
383}
384
385impl Default for Version {
386 fn default() -> Self {
387 Self::Minor
388 }
389}
390"#####,
391 )
392}
393
394#[test]
368fn doctest_generate_derive() { 395fn doctest_generate_derive() {
369 check_doc_test( 396 check_doc_test(
370 "generate_derive", 397 "generate_derive",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 01f5c291f..f2cacf7c8 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -212,6 +212,10 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
212 ast::Expr::BinExpr(bin) => match bin.op_kind()? { 212 ast::Expr::BinExpr(bin) => match bin.op_kind()? {
213 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()), 213 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
214 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()), 214 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()),
215 // Parenthesize composite boolean expressions before prefixing `!`
216 ast::BinOp::BooleanAnd | ast::BinOp::BooleanOr => {
217 Some(make::expr_prefix(T![!], make::expr_paren(expr.clone())))
218 }
215 _ => None, 219 _ => None,
216 }, 220 },
217 ast::Expr::MethodCallExpr(mce) => { 221 ast::Expr::MethodCallExpr(mce) => {
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index ecf3194c6..11ae63c31 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -129,7 +129,7 @@ impl HasSource for TypeParam {
129 type Ast = Either<ast::Trait, ast::TypeParam>; 129 type Ast = Either<ast::Trait, ast::TypeParam>;
130 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> { 130 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
131 let child_source = self.id.parent.child_source(db.upcast()); 131 let child_source = self.id.parent.child_source(db.upcast());
132 child_source.map(|it| it.type_params[self.id.local_id].clone()) 132 child_source.map(|it| it[self.id.local_id].clone())
133 } 133 }
134} 134}
135 135
@@ -137,6 +137,6 @@ impl HasSource for LifetimeParam {
137 type Ast = ast::LifetimeParam; 137 type Ast = ast::LifetimeParam;
138 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> { 138 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
139 let child_source = self.id.parent.child_source(db.upcast()); 139 let child_source = self.id.parent.child_source(db.upcast());
140 child_source.map(|it| it.lifetime_params[self.id.local_id].clone()) 140 child_source.map(|it| it[self.id.local_id].clone())
141 } 141 }
142} 142}
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs
index 6539959c3..eafa3abb6 100644
--- a/crates/hir_def/src/adt.rs
+++ b/crates/hir_def/src/adt.rs
@@ -145,10 +145,12 @@ impl EnumData {
145 } 145 }
146} 146}
147 147
148impl HasChildSource for EnumId { 148impl HasChildSource<LocalEnumVariantId> for EnumId {
149 type ChildId = LocalEnumVariantId;
150 type Value = ast::Variant; 149 type Value = ast::Variant;
151 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> { 150 fn child_source(
151 &self,
152 db: &dyn DefDatabase,
153 ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
152 let src = self.lookup(db).source(db); 154 let src = self.lookup(db).source(db);
153 let mut trace = Trace::new_for_map(); 155 let mut trace = Trace::new_for_map();
154 lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db)); 156 lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db));
@@ -212,11 +214,10 @@ impl VariantData {
212 } 214 }
213} 215}
214 216
215impl HasChildSource for VariantId { 217impl HasChildSource<LocalFieldId> for VariantId {
216 type ChildId = LocalFieldId;
217 type Value = Either<ast::TupleField, ast::RecordField>; 218 type Value = Either<ast::TupleField, ast::RecordField>;
218 219
219 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> { 220 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
220 let (src, module_id) = match self { 221 let (src, module_id) = match self {
221 VariantId::EnumVariantId(it) => { 222 VariantId::EnumVariantId(it) => {
222 // I don't really like the fact that we call into parent source 223 // I don't really like the fact that we call into parent source
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 81912a454..924046435 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -19,7 +19,7 @@ use crate::{
19 db::DefDatabase, 19 db::DefDatabase,
20 dyn_map::DynMap, 20 dyn_map::DynMap,
21 keys, 21 keys,
22 src::HasSource, 22 src::{HasChildSource, HasSource},
23 type_ref::{LifetimeRef, TypeBound, TypeRef}, 23 type_ref::{LifetimeRef, TypeBound, TypeRef},
24 AdtId, GenericDefId, LifetimeParamId, LocalLifetimeParamId, LocalTypeParamId, Lookup, 24 AdtId, GenericDefId, LifetimeParamId, LocalLifetimeParamId, LocalTypeParamId, Lookup,
25 TypeParamId, 25 TypeParamId,
@@ -73,9 +73,9 @@ pub enum WherePredicateTypeTarget {
73} 73}
74 74
75#[derive(Default)] 75#[derive(Default)]
76pub struct SourceMaps { 76pub(crate) struct SourceMap {
77 pub type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>, 77 pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>,
78 pub lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>, 78 lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>,
79} 79}
80 80
81impl GenericParams { 81impl GenericParams {
@@ -133,9 +133,9 @@ impl GenericParams {
133 Arc::new(generics) 133 Arc::new(generics)
134 } 134 }
135 135
136 fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMaps>) { 136 fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
137 let mut generics = GenericParams::default(); 137 let mut generics = GenericParams::default();
138 let mut sm = SourceMaps::default(); 138 let mut sm = SourceMap::default();
139 139
140 // FIXME: add `: Sized` bound for everything except for `Self` in traits 140 // FIXME: add `: Sized` bound for everything except for `Self` in traits
141 let file_id = match def { 141 let file_id = match def {
@@ -214,7 +214,7 @@ impl GenericParams {
214 pub(crate) fn fill( 214 pub(crate) fn fill(
215 &mut self, 215 &mut self,
216 lower_ctx: &LowerCtx, 216 lower_ctx: &LowerCtx,
217 sm: &mut SourceMaps, 217 sm: &mut SourceMap,
218 node: &dyn GenericParamsOwner, 218 node: &dyn GenericParamsOwner,
219 ) { 219 ) {
220 if let Some(params) = node.generic_param_list() { 220 if let Some(params) = node.generic_param_list() {
@@ -241,7 +241,7 @@ impl GenericParams {
241 fn fill_params( 241 fn fill_params(
242 &mut self, 242 &mut self,
243 lower_ctx: &LowerCtx, 243 lower_ctx: &LowerCtx,
244 sm: &mut SourceMaps, 244 sm: &mut SourceMap,
245 params: ast::GenericParamList, 245 params: ast::GenericParamList,
246 ) { 246 ) {
247 for type_param in params.type_params() { 247 for type_param in params.type_params() {
@@ -345,10 +345,24 @@ impl GenericParams {
345 }) 345 })
346 } 346 }
347} 347}
348impl GenericDefId { 348
349 // FIXME: Change HasChildSource's ChildId AssocItem to be a generic parameter instead 349impl HasChildSource<LocalTypeParamId> for GenericDefId {
350 pub fn child_source(&self, db: &dyn DefDatabase) -> InFile<SourceMaps> { 350 type Value = Either<ast::Trait, ast::TypeParam>;
351 GenericParams::new(db, *self).1 351 fn child_source(
352 &self,
353 db: &dyn DefDatabase,
354 ) -> InFile<ArenaMap<LocalTypeParamId, Self::Value>> {
355 GenericParams::new(db, *self).1.map(|source_maps| source_maps.type_params)
356 }
357}
358
359impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
360 type Value = ast::LifetimeParam;
361 fn child_source(
362 &self,
363 db: &dyn DefDatabase,
364 ) -> InFile<ArenaMap<LocalLifetimeParamId, Self::Value>> {
365 GenericParams::new(db, *self).1.map(|source_maps| source_maps.lifetime_params)
352 } 366 }
353} 367}
354 368
diff --git a/crates/hir_def/src/src.rs b/crates/hir_def/src/src.rs
index 7a79b0314..f67244b46 100644
--- a/crates/hir_def/src/src.rs
+++ b/crates/hir_def/src/src.rs
@@ -36,8 +36,7 @@ impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
36 } 36 }
37} 37}
38 38
39pub trait HasChildSource { 39pub trait HasChildSource<ChildId> {
40 type ChildId;
41 type Value; 40 type Value;
42 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>>; 41 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<ChildId, Self::Value>>;
43} 42}
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index cc09b77a5..16b079c42 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -196,6 +196,9 @@ pub fn expr_method_call(receiver: ast::Expr, method: &str, arg_list: ast::ArgLis
196pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr { 196pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
197 expr_from_text(&if exclusive { format!("&mut {}", expr) } else { format!("&{}", expr) }) 197 expr_from_text(&if exclusive { format!("&mut {}", expr) } else { format!("&{}", expr) })
198} 198}
199pub fn expr_paren(expr: ast::Expr) -> ast::Expr {
200 expr_from_text(&format!("({})", expr))
201}
199fn expr_from_text(text: &str) -> ast::Expr { 202fn expr_from_text(text: &str) -> ast::Expr {
200 ast_from_text(&format!("const C: () = {};", text)) 203 ast_from_text(&format!("const C: () = {};", text))
201} 204}
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 2795f6b5c..4a2f9feb3 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -292,3 +292,17 @@ Release steps:
2924. Tweet 2924. Tweet
2935. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's submodule. 2935. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's submodule.
294 Self-approve the PR. 294 Self-approve the PR.
295
296# Permissions
297
298There are three sets of people with extra permissions:
299
300* rust-analyzer GitHub organization **admins** (which include current t-compiler leads).
301 Admins have full access to the org.
302* **review** team in the organization.
303 Reviewers have `r+` access to all of organization's repositories and publish rights on crates.io.
304 They also have direct commit access, but all changes should via bors queue.
305 It's ok to self-approve if you think you know what you are doing!
306 bors should automatically sync the permissions.
307* **triage** team in the organization.
308 This team can label and close issues.