diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-05-09 16:55:42 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-05-09 16:55:42 +0100 |
commit | a3b034938ebe12b29ef37ff6e54bad3c574464be (patch) | |
tree | 1ed28e5499d4db6948c18f96e2136a2f61f5e222 /crates | |
parent | 0900beeaa2ca4b9e91d51165545935d4e1db7bb6 (diff) | |
parent | 5342800147679a0ded5546322c94aa6339d58fbc (diff) |
Merge #8781
8781: internal: rewrite **Repalce impl Trait** assist to mutable syntax trees r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ide/src/join_lines.rs | 36 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/extract_variable.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/introduce_named_lifetime.rs | 4 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs | 120 | ||||
-rw-r--r-- | crates/ide_assists/src/utils/suggest_name.rs | 15 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit_in_place.rs | 19 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 4 |
7 files changed, 100 insertions, 100 deletions
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index 482b23cf5..61dcbb399 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::convert::TryFrom; | ||
2 | |||
1 | use ide_assists::utils::extract_trivial_expression; | 3 | use ide_assists::utils::extract_trivial_expression; |
2 | use itertools::Itertools; | 4 | use itertools::Itertools; |
3 | use syntax::{ | 5 | use syntax::{ |
@@ -65,6 +67,14 @@ fn remove_newlines(edit: &mut TextEditBuilder, token: &SyntaxToken, range: TextR | |||
65 | 67 | ||
66 | fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) { | 68 | fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) { |
67 | if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 { | 69 | if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 { |
70 | let n_spaces_after_line_break = { | ||
71 | let suff = &token.text()[TextRange::new( | ||
72 | offset - token.text_range().start() + TextSize::of('\n'), | ||
73 | TextSize::of(token.text()), | ||
74 | )]; | ||
75 | suff.bytes().take_while(|&b| b == b' ').count() | ||
76 | }; | ||
77 | |||
68 | let mut no_space = false; | 78 | let mut no_space = false; |
69 | if let Some(string) = ast::String::cast(token.clone()) { | 79 | if let Some(string) = ast::String::cast(token.clone()) { |
70 | if let Some(range) = string.open_quote_text_range() { | 80 | if let Some(range) = string.open_quote_text_range() { |
@@ -73,18 +83,13 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS | |||
73 | } | 83 | } |
74 | if let Some(range) = string.close_quote_text_range() { | 84 | if let Some(range) = string.close_quote_text_range() { |
75 | cov_mark::hit!(join_string_literal_close_quote); | 85 | cov_mark::hit!(join_string_literal_close_quote); |
76 | no_space |= range.start() == offset + TextSize::of('\n'); | 86 | no_space |= range.start() |
87 | == offset | ||
88 | + TextSize::of('\n') | ||
89 | + TextSize::try_from(n_spaces_after_line_break).unwrap(); | ||
77 | } | 90 | } |
78 | } | 91 | } |
79 | 92 | ||
80 | let n_spaces_after_line_break = { | ||
81 | let suff = &token.text()[TextRange::new( | ||
82 | offset - token.text_range().start() + TextSize::of('\n'), | ||
83 | TextSize::of(token.text()), | ||
84 | )]; | ||
85 | suff.bytes().take_while(|&b| b == b' ').count() | ||
86 | }; | ||
87 | |||
88 | let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into()); | 93 | let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into()); |
89 | let replace_with = if no_space { "" } else { " " }; | 94 | let replace_with = if no_space { "" } else { " " }; |
90 | edit.replace(range, replace_with.to_string()); | 95 | edit.replace(range, replace_with.to_string()); |
@@ -835,6 +840,19 @@ fn main() { | |||
835 | } | 840 | } |
836 | "#, | 841 | "#, |
837 | ); | 842 | ); |
843 | check_join_lines( | ||
844 | r#" | ||
845 | fn main() { | ||
846 | $0r"hello | ||
847 | "; | ||
848 | } | ||
849 | "#, | ||
850 | r#" | ||
851 | fn main() { | ||
852 | $0r"hello"; | ||
853 | } | ||
854 | "#, | ||
855 | ); | ||
838 | } | 856 | } |
839 | 857 | ||
840 | check_join_lines( | 858 | check_join_lines( |
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs index 136b9a55b..ae084c86c 100644 --- a/crates/ide_assists/src/handlers/extract_variable.rs +++ b/crates/ide_assists/src/handlers/extract_variable.rs | |||
@@ -54,7 +54,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
54 | 54 | ||
55 | let var_name = match &field_shorthand { | 55 | let var_name = match &field_shorthand { |
56 | Some(it) => it.to_string(), | 56 | Some(it) => it.to_string(), |
57 | None => suggest_name::variable(&to_extract, &ctx.sema), | 57 | None => suggest_name::for_variable(&to_extract, &ctx.sema), |
58 | }; | 58 | }; |
59 | let expr_range = match &field_shorthand { | 59 | let expr_range = match &field_shorthand { |
60 | Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), | 60 | Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), |
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs index 9f4f71d6c..cb71ca8bd 100644 --- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs | |||
@@ -139,12 +139,12 @@ fn generate_unique_lifetime_param_name( | |||
139 | 139 | ||
140 | fn add_lifetime_param(type_params: ast::GenericParamList, new_lifetime_param: char) { | 140 | fn add_lifetime_param(type_params: ast::GenericParamList, new_lifetime_param: char) { |
141 | let generic_param = | 141 | let generic_param = |
142 | make::generic_param(format!("'{}", new_lifetime_param), None).clone_for_update(); | 142 | make::generic_param(&format!("'{}", new_lifetime_param), None).clone_for_update(); |
143 | type_params.add_generic_param(generic_param); | 143 | type_params.add_generic_param(generic_param); |
144 | } | 144 | } |
145 | 145 | ||
146 | fn make_ast_lifetime(new_lifetime_param: char) -> ast::Lifetime { | 146 | fn make_ast_lifetime(new_lifetime_param: char) -> ast::Lifetime { |
147 | make::generic_param(format!("'{}", new_lifetime_param), None) | 147 | make::generic_param(&format!("'{}", new_lifetime_param), None) |
148 | .syntax() | 148 | .syntax() |
149 | .descendants() | 149 | .descendants() |
150 | .find_map(ast::Lifetime::cast) | 150 | .find_map(ast::Lifetime::cast) |
diff --git a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs index ff25b61ea..16cae0281 100644 --- a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner}; | 1 | use syntax::{ |
2 | ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode}, | ||
3 | ted, | ||
4 | }; | ||
2 | 5 | ||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; |
4 | 7 | ||
5 | // Assist: replace_impl_trait_with_generic | 8 | // Assist: replace_impl_trait_with_generic |
6 | // | 9 | // |
@@ -17,30 +20,29 @@ pub(crate) fn replace_impl_trait_with_generic( | |||
17 | acc: &mut Assists, | 20 | acc: &mut Assists, |
18 | ctx: &AssistContext, | 21 | ctx: &AssistContext, |
19 | ) -> Option<()> { | 22 | ) -> Option<()> { |
20 | let type_impl_trait = ctx.find_node_at_offset::<ast::ImplTraitType>()?; | 23 | let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?; |
21 | let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; | 24 | let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?; |
22 | let type_fn = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; | 25 | let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?; |
23 | 26 | ||
24 | let impl_trait_ty = type_impl_trait.type_bound_list()?; | 27 | let type_bound_list = impl_trait_type.type_bound_list()?; |
25 | 28 | ||
26 | let target = type_fn.syntax().text_range(); | 29 | let target = fn_.syntax().text_range(); |
27 | acc.add( | 30 | acc.add( |
28 | AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite), | 31 | AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite), |
29 | "Replace impl trait with generic", | 32 | "Replace impl trait with generic", |
30 | target, | 33 | target, |
31 | |edit| { | 34 | |edit| { |
32 | let generic_letter = impl_trait_ty.to_string().chars().next().unwrap().to_string(); | 35 | let impl_trait_type = edit.make_ast_mut(impl_trait_type); |
36 | let fn_ = edit.make_ast_mut(fn_); | ||
33 | 37 | ||
34 | let generic_param_list = type_fn | 38 | let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type); |
35 | .generic_param_list() | ||
36 | .unwrap_or_else(|| make::generic_param_list(None)) | ||
37 | .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty))); | ||
38 | 39 | ||
39 | let new_type_fn = type_fn | 40 | let type_param = |
40 | .replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter)) | 41 | make::generic_param(&type_param_name, Some(type_bound_list)).clone_for_update(); |
41 | .with_generic_param_list(generic_param_list); | 42 | let new_ty = make::ty(&type_param_name).clone_for_update(); |
42 | 43 | ||
43 | edit.replace_ast(type_fn.clone(), new_type_fn); | 44 | ted::replace(impl_trait_type.syntax(), new_ty.syntax()); |
45 | fn_.get_or_create_generic_param_list().add_generic_param(type_param) | ||
44 | }, | 46 | }, |
45 | ) | 47 | ) |
46 | } | 48 | } |
@@ -55,12 +57,8 @@ mod tests { | |||
55 | fn replace_impl_trait_with_generic_params() { | 57 | fn replace_impl_trait_with_generic_params() { |
56 | check_assist( | 58 | check_assist( |
57 | replace_impl_trait_with_generic, | 59 | replace_impl_trait_with_generic, |
58 | r#" | 60 | r#"fn foo<G>(bar: $0impl Bar) {}"#, |
59 | fn foo<G>(bar: $0impl Bar) {} | 61 | r#"fn foo<G, B: Bar>(bar: B) {}"#, |
60 | "#, | ||
61 | r#" | ||
62 | fn foo<G, B: Bar>(bar: B) {} | ||
63 | "#, | ||
64 | ); | 62 | ); |
65 | } | 63 | } |
66 | 64 | ||
@@ -68,12 +66,8 @@ mod tests { | |||
68 | fn replace_impl_trait_without_generic_params() { | 66 | fn replace_impl_trait_without_generic_params() { |
69 | check_assist( | 67 | check_assist( |
70 | replace_impl_trait_with_generic, | 68 | replace_impl_trait_with_generic, |
71 | r#" | 69 | r#"fn foo(bar: $0impl Bar) {}"#, |
72 | fn foo(bar: $0impl Bar) {} | 70 | r#"fn foo<B: Bar>(bar: B) {}"#, |
73 | "#, | ||
74 | r#" | ||
75 | fn foo<B: Bar>(bar: B) {} | ||
76 | "#, | ||
77 | ); | 71 | ); |
78 | } | 72 | } |
79 | 73 | ||
@@ -81,12 +75,8 @@ mod tests { | |||
81 | fn replace_two_impl_trait_with_generic_params() { | 75 | fn replace_two_impl_trait_with_generic_params() { |
82 | check_assist( | 76 | check_assist( |
83 | replace_impl_trait_with_generic, | 77 | replace_impl_trait_with_generic, |
84 | r#" | 78 | r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#, |
85 | fn foo<G>(foo: impl Foo, bar: $0impl Bar) {} | 79 | r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#, |
86 | "#, | ||
87 | r#" | ||
88 | fn foo<G, B: Bar>(foo: impl Foo, bar: B) {} | ||
89 | "#, | ||
90 | ); | 80 | ); |
91 | } | 81 | } |
92 | 82 | ||
@@ -94,12 +84,8 @@ mod tests { | |||
94 | fn replace_impl_trait_with_empty_generic_params() { | 84 | fn replace_impl_trait_with_empty_generic_params() { |
95 | check_assist( | 85 | check_assist( |
96 | replace_impl_trait_with_generic, | 86 | replace_impl_trait_with_generic, |
97 | r#" | 87 | r#"fn foo<>(bar: $0impl Bar) {}"#, |
98 | fn foo<>(bar: $0impl Bar) {} | 88 | r#"fn foo<B: Bar>(bar: B) {}"#, |
99 | "#, | ||
100 | r#" | ||
101 | fn foo<B: Bar>(bar: B) {} | ||
102 | "#, | ||
103 | ); | 89 | ); |
104 | } | 90 | } |
105 | 91 | ||
@@ -108,13 +94,13 @@ mod tests { | |||
108 | check_assist( | 94 | check_assist( |
109 | replace_impl_trait_with_generic, | 95 | replace_impl_trait_with_generic, |
110 | r#" | 96 | r#" |
111 | fn foo< | 97 | fn foo< |
112 | >(bar: $0impl Bar) {} | 98 | >(bar: $0impl Bar) {} |
113 | "#, | 99 | "#, |
114 | r#" | 100 | r#" |
115 | fn foo<B: Bar | 101 | fn foo<B: Bar |
116 | >(bar: B) {} | 102 | >(bar: B) {} |
117 | "#, | 103 | "#, |
118 | ); | 104 | ); |
119 | } | 105 | } |
120 | 106 | ||
@@ -123,12 +109,8 @@ mod tests { | |||
123 | fn replace_impl_trait_with_exist_generic_letter() { | 109 | fn replace_impl_trait_with_exist_generic_letter() { |
124 | check_assist( | 110 | check_assist( |
125 | replace_impl_trait_with_generic, | 111 | replace_impl_trait_with_generic, |
126 | r#" | 112 | r#"fn foo<B>(bar: $0impl Bar) {}"#, |
127 | fn foo<B>(bar: $0impl Bar) {} | 113 | r#"fn foo<B, C: Bar>(bar: C) {}"#, |
128 | "#, | ||
129 | r#" | ||
130 | fn foo<B, C: Bar>(bar: C) {} | ||
131 | "#, | ||
132 | ); | 114 | ); |
133 | } | 115 | } |
134 | 116 | ||
@@ -137,19 +119,19 @@ mod tests { | |||
137 | check_assist( | 119 | check_assist( |
138 | replace_impl_trait_with_generic, | 120 | replace_impl_trait_with_generic, |
139 | r#" | 121 | r#" |
140 | fn foo< | 122 | fn foo< |
141 | G: Foo, | 123 | G: Foo, |
142 | F, | 124 | F, |
143 | H, | 125 | H, |
144 | >(bar: $0impl Bar) {} | 126 | >(bar: $0impl Bar) {} |
145 | "#, | 127 | "#, |
146 | r#" | 128 | r#" |
147 | fn foo< | 129 | fn foo< |
148 | G: Foo, | 130 | G: Foo, |
149 | F, | 131 | F, |
150 | H, B: Bar | 132 | H, B: Bar, |
151 | >(bar: B) {} | 133 | >(bar: B) {} |
152 | "#, | 134 | "#, |
153 | ); | 135 | ); |
154 | } | 136 | } |
155 | 137 | ||
@@ -157,12 +139,8 @@ mod tests { | |||
157 | fn replace_impl_trait_multiple() { | 139 | fn replace_impl_trait_multiple() { |
158 | check_assist( | 140 | check_assist( |
159 | replace_impl_trait_with_generic, | 141 | replace_impl_trait_with_generic, |
160 | r#" | 142 | r#"fn foo(bar: $0impl Foo + Bar) {}"#, |
161 | fn foo(bar: $0impl Foo + Bar) {} | 143 | r#"fn foo<F: Foo + Bar>(bar: F) {}"#, |
162 | "#, | ||
163 | r#" | ||
164 | fn foo<F: Foo + Bar>(bar: F) {} | ||
165 | "#, | ||
166 | ); | 144 | ); |
167 | } | 145 | } |
168 | } | 146 | } |
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs index deafcd630..b3aabeab3 100644 --- a/crates/ide_assists/src/utils/suggest_name.rs +++ b/crates/ide_assists/src/utils/suggest_name.rs | |||
@@ -6,7 +6,7 @@ use itertools::Itertools; | |||
6 | use stdx::to_lower_snake_case; | 6 | use stdx::to_lower_snake_case; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | ast::{self, NameOwner}, | 8 | ast::{self, NameOwner}, |
9 | match_ast, AstNode, | 9 | match_ast, AstNode, SmolStr, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` | 12 | /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` |
@@ -57,6 +57,14 @@ const USELESS_METHODS: &[&str] = &[ | |||
57 | "iter_mut", | 57 | "iter_mut", |
58 | ]; | 58 | ]; |
59 | 59 | ||
60 | pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { | ||
61 | let c = ty | ||
62 | .type_bound_list() | ||
63 | .and_then(|bounds| bounds.syntax().text().char_at(0.into())) | ||
64 | .unwrap_or('T'); | ||
65 | c.encode_utf8(&mut [0; 4]).into() | ||
66 | } | ||
67 | |||
60 | /// Suggest name of variable for given expression | 68 | /// Suggest name of variable for given expression |
61 | /// | 69 | /// |
62 | /// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. | 70 | /// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. |
@@ -75,7 +83,8 @@ const USELESS_METHODS: &[&str] = &[ | |||
75 | /// It also applies heuristics to filter out less informative names | 83 | /// It also applies heuristics to filter out less informative names |
76 | /// | 84 | /// |
77 | /// Currently it sticks to the first name found. | 85 | /// Currently it sticks to the first name found. |
78 | pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { | 86 | // FIXME: Microoptimize and return a `SmolStr` here. |
87 | pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { | ||
79 | // `from_param` does not benifit from stripping | 88 | // `from_param` does not benifit from stripping |
80 | // it need the largest context possible | 89 | // it need the largest context possible |
81 | // so we check firstmost | 90 | // so we check firstmost |
@@ -276,7 +285,7 @@ mod tests { | |||
276 | frange.range, | 285 | frange.range, |
277 | "selection is not an expression(yet contained in one)" | 286 | "selection is not an expression(yet contained in one)" |
278 | ); | 287 | ); |
279 | let name = variable(&expr, &sema); | 288 | let name = for_variable(&expr, &sema); |
280 | assert_eq!(&name, expected); | 289 | assert_eq!(&name, expected); |
281 | } | 290 | } |
282 | 291 | ||
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 04f97f368..168355555 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs | |||
@@ -195,18 +195,13 @@ impl ast::GenericParamList { | |||
195 | pub fn add_generic_param(&self, generic_param: ast::GenericParam) { | 195 | pub fn add_generic_param(&self, generic_param: ast::GenericParam) { |
196 | match self.generic_params().last() { | 196 | match self.generic_params().last() { |
197 | Some(last_param) => { | 197 | Some(last_param) => { |
198 | let mut elems = Vec::new(); | 198 | let position = Position::after(last_param.syntax()); |
199 | if !last_param | 199 | let elements = vec![ |
200 | .syntax() | 200 | make::token(T![,]).into(), |
201 | .siblings_with_tokens(Direction::Next) | 201 | make::tokens::single_space().into(), |
202 | .any(|it| it.kind() == T![,]) | 202 | generic_param.syntax().clone().into(), |
203 | { | 203 | ]; |
204 | elems.push(make::token(T![,]).into()); | 204 | ted::insert_all(position, elements); |
205 | elems.push(make::tokens::single_space().into()); | ||
206 | }; | ||
207 | elems.push(generic_param.syntax().clone().into()); | ||
208 | let after_last_param = Position::after(last_param.syntax()); | ||
209 | ted::insert_all(after_last_param, elems); | ||
210 | } | 205 | } |
211 | None => { | 206 | None => { |
212 | let after_l_angle = Position::after(self.l_angle_token().unwrap()); | 207 | let after_l_angle = Position::after(self.l_angle_token().unwrap()); |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 5a6687397..2289d8f3e 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -475,8 +475,8 @@ pub fn param_list( | |||
475 | }; | 475 | }; |
476 | ast_from_text(&list) | 476 | ast_from_text(&list) |
477 | } | 477 | } |
478 | 478 | // FIXME: s/&str/ast:Name | |
479 | pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { | 479 | pub fn generic_param(name: &str, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { |
480 | let bound = match ty { | 480 | let bound = match ty { |
481 | Some(it) => format!(": {}", it), | 481 | Some(it) => format!(": {}", it), |
482 | None => String::new(), | 482 | None => String::new(), |