diff options
-rw-r--r-- | crates/assists/src/handlers/generate_default_from_enum_variant.rs | 175 | ||||
-rw-r--r-- | crates/assists/src/handlers/invert_if.rs | 9 | ||||
-rw-r--r-- | crates/assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/assists/src/tests/generated.rs | 27 | ||||
-rw-r--r-- | crates/assists/src/utils.rs | 4 | ||||
-rw-r--r-- | crates/hir/src/has_source.rs | 4 | ||||
-rw-r--r-- | crates/hir_def/src/adt.rs | 13 | ||||
-rw-r--r-- | crates/hir_def/src/generics.rs | 38 | ||||
-rw-r--r-- | crates/hir_def/src/src.rs | 5 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 3 | ||||
-rw-r--r-- | docs/dev/README.md | 14 |
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 @@ | |||
1 | use ide_db::helpers::FamousDefs; | ||
2 | use ide_db::RootDatabase; | ||
3 | use syntax::ast::{self, AstNode, NameOwner}; | ||
4 | use test_utils::mark; | ||
5 | |||
6 | use 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 | // ``` | ||
33 | pub(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 | |||
60 | impl 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 | |||
72 | fn 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)] | ||
91 | mod 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#" | ||
109 | enum Variant { | ||
110 | Undefined, | ||
111 | Minor<|>, | ||
112 | Major, | ||
113 | }"#, | ||
114 | r#"enum Variant { | ||
115 | Undefined, | ||
116 | Minor, | ||
117 | Major, | ||
118 | } | ||
119 | |||
120 | impl 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#" | ||
133 | enum Variant { | ||
134 | Undefined, | ||
135 | Minor<|>, | ||
136 | Major, | ||
137 | } | ||
138 | |||
139 | impl 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#" | ||
152 | enum 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#" | ||
166 | enum Variant { Undefined } | ||
167 | |||
168 | impl 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] |
368 | fn doctest_generate_default_from_enum_variant() { | ||
369 | check_doc_test( | ||
370 | "generate_default_from_enum_variant", | ||
371 | r#####" | ||
372 | enum Version { | ||
373 | Undefined, | ||
374 | Minor<|>, | ||
375 | Major, | ||
376 | } | ||
377 | "#####, | ||
378 | r#####" | ||
379 | enum Version { | ||
380 | Undefined, | ||
381 | Minor, | ||
382 | Major, | ||
383 | } | ||
384 | |||
385 | impl Default for Version { | ||
386 | fn default() -> Self { | ||
387 | Self::Minor | ||
388 | } | ||
389 | } | ||
390 | "#####, | ||
391 | ) | ||
392 | } | ||
393 | |||
394 | #[test] | ||
368 | fn doctest_generate_derive() { | 395 | fn 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 | ||
148 | impl HasChildSource for EnumId { | 148 | impl 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 | ||
215 | impl HasChildSource for VariantId { | 217 | impl 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)] |
76 | pub struct SourceMaps { | 76 | pub(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 | ||
81 | impl GenericParams { | 81 | impl 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 | } |
348 | impl GenericDefId { | 348 | |
349 | // FIXME: Change HasChildSource's ChildId AssocItem to be a generic parameter instead | 349 | impl 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 | |||
359 | impl 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 | ||
39 | pub trait HasChildSource { | 39 | pub 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 | |||
196 | pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr { | 196 | pub 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 | } |
199 | pub fn expr_paren(expr: ast::Expr) -> ast::Expr { | ||
200 | expr_from_text(&format!("({})", expr)) | ||
201 | } | ||
199 | fn expr_from_text(text: &str) -> ast::Expr { | 202 | fn 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: | |||
292 | 4. Tweet | 292 | 4. Tweet |
293 | 5. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's submodule. | 293 | 5. 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 | |||
298 | There 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. | ||