diff options
Diffstat (limited to 'crates/assists/src')
-rw-r--r-- | crates/assists/src/handlers/auto_import.rs | 2 | ||||
-rw-r--r-- | crates/assists/src/handlers/qualify_path.rs | 124 | ||||
-rw-r--r-- | crates/assists/src/tests/generated.rs | 19 | ||||
-rw-r--r-- | crates/assists/src/utils/import_assets.rs | 40 |
4 files changed, 115 insertions, 70 deletions
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs index e595b5b93..13e390a1f 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs | |||
@@ -45,7 +45,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
45 | let group = import_group_message(import_assets.import_candidate()); | 45 | let group = import_group_message(import_assets.import_candidate()); |
46 | let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?; | 46 | let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?; |
47 | let syntax = scope.as_syntax_node(); | 47 | let syntax = scope.as_syntax_node(); |
48 | for import in proposed_imports { | 48 | for (import, _) in proposed_imports { |
49 | acc.add_group( | 49 | acc.add_group( |
50 | &group, | 50 | &group, |
51 | AssistId("auto_import", AssistKind::QuickFix), | 51 | AssistId("auto_import", AssistKind::QuickFix), |
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs index bbbf47bef..479ff498c 100644 --- a/crates/assists/src/handlers/qualify_path.rs +++ b/crates/assists/src/handlers/qualify_path.rs | |||
@@ -1,6 +1,12 @@ | |||
1 | use std::collections::BTreeSet; | 1 | use std::iter; |
2 | 2 | ||
3 | use syntax::{ast, AstNode, TextRange}; | 3 | use hir::AsName; |
4 | use ide_db::RootDatabase; | ||
5 | use syntax::{ | ||
6 | ast, | ||
7 | ast::{make, ArgListOwner}, | ||
8 | AstNode, TextRange, | ||
9 | }; | ||
4 | use test_utils::mark; | 10 | use test_utils::mark; |
5 | 11 | ||
6 | use crate::{ | 12 | use crate::{ |
@@ -10,6 +16,8 @@ use crate::{ | |||
10 | AssistId, AssistKind, GroupLabel, | 16 | AssistId, AssistKind, GroupLabel, |
11 | }; | 17 | }; |
12 | 18 | ||
19 | const ASSIST_ID: AssistId = AssistId("qualify_path", AssistKind::QuickFix); | ||
20 | |||
13 | // Assist: qualify_path | 21 | // Assist: qualify_path |
14 | // | 22 | // |
15 | // If the name is unresolved, provides all possible qualified paths for it. | 23 | // If the name is unresolved, provides all possible qualified paths for it. |
@@ -53,30 +61,14 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
53 | ImportCandidate::UnqualifiedName(candidate) => { | 61 | ImportCandidate::UnqualifiedName(candidate) => { |
54 | qualify_path_unqualified_name(acc, proposed_imports, range, &candidate.name) | 62 | qualify_path_unqualified_name(acc, proposed_imports, range, &candidate.name) |
55 | } | 63 | } |
56 | ImportCandidate::TraitAssocItem(candidate) => { | 64 | ImportCandidate::TraitAssocItem(_) => { |
57 | let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; | 65 | let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; |
58 | let (qualifier, segment) = (path.qualifier()?, path.segment()?); | 66 | let (qualifier, segment) = (path.qualifier()?, path.segment()?); |
59 | qualify_path_trait_assoc_item( | 67 | qualify_path_trait_assoc_item(acc, proposed_imports, range, qualifier, segment) |
60 | acc, | ||
61 | proposed_imports, | ||
62 | range, | ||
63 | qualifier, | ||
64 | segment, | ||
65 | &candidate.name, | ||
66 | ) | ||
67 | } | 68 | } |
68 | ImportCandidate::TraitMethod(candidate) => { | 69 | ImportCandidate::TraitMethod(_) => { |
69 | let mcall_expr = ast::MethodCallExpr::cast(import_assets.syntax_under_caret().clone())?; | 70 | let mcall_expr = ast::MethodCallExpr::cast(import_assets.syntax_under_caret().clone())?; |
70 | let receiver = mcall_expr.receiver()?; | 71 | qualify_path_trait_method(acc, ctx.sema.db, proposed_imports, range, mcall_expr)?; |
71 | let name_ref = mcall_expr.name_ref()?; | ||
72 | qualify_path_trait_method( | ||
73 | acc, | ||
74 | proposed_imports, | ||
75 | range, | ||
76 | receiver, | ||
77 | name_ref, | ||
78 | &candidate.name, | ||
79 | ) | ||
80 | } | 72 | } |
81 | }; | 73 | }; |
82 | Some(()) | 74 | Some(()) |
@@ -85,17 +77,17 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
85 | // a test that covers this -> `associated_struct_const` | 77 | // a test that covers this -> `associated_struct_const` |
86 | fn qualify_path_qualifier_start( | 78 | fn qualify_path_qualifier_start( |
87 | acc: &mut Assists, | 79 | acc: &mut Assists, |
88 | proposed_imports: BTreeSet<hir::ModPath>, | 80 | proposed_imports: Vec<(hir::ModPath, hir::ItemInNs)>, |
89 | range: TextRange, | 81 | range: TextRange, |
90 | segment: ast::PathSegment, | 82 | segment: ast::PathSegment, |
91 | qualifier_start: &str, | 83 | qualifier_start: &ast::NameRef, |
92 | ) { | 84 | ) { |
93 | mark::hit!(qualify_path_qualifier_start); | 85 | mark::hit!(qualify_path_qualifier_start); |
94 | let group_label = GroupLabel(format!("Qualify {}", qualifier_start)); | 86 | let group_label = GroupLabel(format!("Qualify {}", qualifier_start)); |
95 | for import in proposed_imports { | 87 | for (import, _) in proposed_imports { |
96 | acc.add_group( | 88 | acc.add_group( |
97 | &group_label, | 89 | &group_label, |
98 | AssistId("qualify_path", AssistKind::QuickFix), | 90 | ASSIST_ID, |
99 | format!("Qualify with `{}`", &import), | 91 | format!("Qualify with `{}`", &import), |
100 | range, | 92 | range, |
101 | |builder| { | 93 | |builder| { |
@@ -109,16 +101,16 @@ fn qualify_path_qualifier_start( | |||
109 | // a test that covers this -> `applicable_when_found_an_import_partial` | 101 | // a test that covers this -> `applicable_when_found_an_import_partial` |
110 | fn qualify_path_unqualified_name( | 102 | fn qualify_path_unqualified_name( |
111 | acc: &mut Assists, | 103 | acc: &mut Assists, |
112 | proposed_imports: BTreeSet<hir::ModPath>, | 104 | proposed_imports: Vec<(hir::ModPath, hir::ItemInNs)>, |
113 | range: TextRange, | 105 | range: TextRange, |
114 | name: &str, | 106 | name: &ast::NameRef, |
115 | ) { | 107 | ) { |
116 | mark::hit!(qualify_path_unqualified_name); | 108 | mark::hit!(qualify_path_unqualified_name); |
117 | let group_label = GroupLabel(format!("Qualify {}", name)); | 109 | let group_label = GroupLabel(format!("Qualify {}", name)); |
118 | for import in proposed_imports { | 110 | for (import, _) in proposed_imports { |
119 | acc.add_group( | 111 | acc.add_group( |
120 | &group_label, | 112 | &group_label, |
121 | AssistId("qualify_path", AssistKind::QuickFix), | 113 | ASSIST_ID, |
122 | format!("Qualify as `{}`", &import), | 114 | format!("Qualify as `{}`", &import), |
123 | range, | 115 | range, |
124 | |builder| builder.replace(range, mod_path_to_ast(&import).to_string()), | 116 | |builder| builder.replace(range, mod_path_to_ast(&import).to_string()), |
@@ -129,18 +121,17 @@ fn qualify_path_unqualified_name( | |||
129 | // a test that covers this -> `associated_trait_const` | 121 | // a test that covers this -> `associated_trait_const` |
130 | fn qualify_path_trait_assoc_item( | 122 | fn qualify_path_trait_assoc_item( |
131 | acc: &mut Assists, | 123 | acc: &mut Assists, |
132 | proposed_imports: BTreeSet<hir::ModPath>, | 124 | proposed_imports: Vec<(hir::ModPath, hir::ItemInNs)>, |
133 | range: TextRange, | 125 | range: TextRange, |
134 | qualifier: ast::Path, | 126 | qualifier: ast::Path, |
135 | segment: ast::PathSegment, | 127 | segment: ast::PathSegment, |
136 | trait_assoc_item_name: &str, | ||
137 | ) { | 128 | ) { |
138 | mark::hit!(qualify_path_trait_assoc_item); | 129 | mark::hit!(qualify_path_trait_assoc_item); |
139 | let group_label = GroupLabel(format!("Qualify {}", trait_assoc_item_name)); | 130 | let group_label = GroupLabel(format!("Qualify {}", &segment)); |
140 | for import in proposed_imports { | 131 | for (import, _) in proposed_imports { |
141 | acc.add_group( | 132 | acc.add_group( |
142 | &group_label, | 133 | &group_label, |
143 | AssistId("qualify_path", AssistKind::QuickFix), | 134 | ASSIST_ID, |
144 | format!("Qualify with cast as `{}`", &import), | 135 | format!("Qualify with cast as `{}`", &import), |
145 | range, | 136 | range, |
146 | |builder| { | 137 | |builder| { |
@@ -154,33 +145,74 @@ fn qualify_path_trait_assoc_item( | |||
154 | // a test that covers this -> `trait_method` | 145 | // a test that covers this -> `trait_method` |
155 | fn qualify_path_trait_method( | 146 | fn qualify_path_trait_method( |
156 | acc: &mut Assists, | 147 | acc: &mut Assists, |
157 | proposed_imports: BTreeSet<hir::ModPath>, | 148 | db: &RootDatabase, |
149 | proposed_imports: Vec<(hir::ModPath, hir::ItemInNs)>, | ||
158 | range: TextRange, | 150 | range: TextRange, |
159 | receiver: ast::Expr, | 151 | mcall_expr: ast::MethodCallExpr, |
160 | name_ref: ast::NameRef, | 152 | ) -> Option<()> { |
161 | trait_method_name: &str, | ||
162 | ) { | ||
163 | mark::hit!(qualify_path_trait_method); | 153 | mark::hit!(qualify_path_trait_method); |
154 | |||
155 | let receiver = mcall_expr.receiver()?; | ||
156 | let trait_method_name = mcall_expr.name_ref()?; | ||
157 | let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args()); | ||
164 | let group_label = GroupLabel(format!("Qualify {}", trait_method_name)); | 158 | let group_label = GroupLabel(format!("Qualify {}", trait_method_name)); |
165 | for import in proposed_imports { | 159 | let find_method = |item: &hir::AssocItem| { |
160 | item.name(db).map(|name| name == trait_method_name.as_name()).unwrap_or(false) | ||
161 | }; | ||
162 | for (import, trait_) in proposed_imports.into_iter().filter_map(filter_trait) { | ||
166 | acc.add_group( | 163 | acc.add_group( |
167 | &group_label, | 164 | &group_label, |
168 | AssistId("qualify_path", AssistKind::QuickFix), // < Does this still count as quickfix? | 165 | ASSIST_ID, |
169 | format!("Qualify `{}`", &import), | 166 | format!("Qualify `{}`", &import), |
170 | range, | 167 | range, |
171 | |builder| { | 168 | |builder| { |
172 | let import = mod_path_to_ast(&import); | 169 | let import = mod_path_to_ast(&import); |
173 | // TODO: check the receiver self type and emit refs accordingly, don't discard other function parameters | 170 | if let Some(hir::AssocItem::Function(method)) = |
174 | builder.replace(range, format!("{}::{}(&{})", import, name_ref, receiver)); | 171 | trait_.items(db).into_iter().find(find_method) |
172 | { | ||
173 | if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) { | ||
174 | let receiver = receiver.clone(); | ||
175 | let receiver = match self_access { | ||
176 | hir::Access::Shared => make::expr_ref(receiver, false), | ||
177 | hir::Access::Exclusive => make::expr_ref(receiver, true), | ||
178 | hir::Access::Owned => receiver, | ||
179 | }; | ||
180 | builder.replace( | ||
181 | range, | ||
182 | format!( | ||
183 | "{}::{}{}", | ||
184 | import, | ||
185 | trait_method_name, | ||
186 | match arg_list.clone() { | ||
187 | Some(args) => make::arg_list(iter::once(receiver).chain(args)), | ||
188 | None => make::arg_list(iter::once(receiver)), | ||
189 | } | ||
190 | ), | ||
191 | ); | ||
192 | } | ||
193 | } | ||
175 | }, | 194 | }, |
176 | ); | 195 | ); |
177 | } | 196 | } |
197 | Some(()) | ||
198 | } | ||
199 | |||
200 | fn filter_trait( | ||
201 | (import, trait_): (hir::ModPath, hir::ItemInNs), | ||
202 | ) -> Option<(hir::ModPath, hir::Trait)> { | ||
203 | if let hir::ModuleDef::Trait(trait_) = hir::ModuleDef::from(trait_.as_module_def_id()?) { | ||
204 | Some((import, trait_)) | ||
205 | } else { | ||
206 | None | ||
207 | } | ||
178 | } | 208 | } |
179 | 209 | ||
180 | #[cfg(test)] | 210 | #[cfg(test)] |
181 | mod tests { | 211 | mod tests { |
182 | use super::*; | ||
183 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 212 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
213 | |||
214 | use super::*; | ||
215 | |||
184 | #[test] | 216 | #[test] |
185 | fn applicable_when_found_an_import_partial() { | 217 | fn applicable_when_found_an_import_partial() { |
186 | mark::check!(qualify_path_unqualified_name); | 218 | mark::check!(qualify_path_unqualified_name); |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index 41f536574..7d5618263 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -713,6 +713,25 @@ fn handle(action: Action) { | |||
713 | } | 713 | } |
714 | 714 | ||
715 | #[test] | 715 | #[test] |
716 | fn doctest_qualify_path() { | ||
717 | check_doc_test( | ||
718 | "qualify_path", | ||
719 | r#####" | ||
720 | fn main() { | ||
721 | let map = HashMap<|>::new(); | ||
722 | } | ||
723 | pub mod std { pub mod collections { pub struct HashMap { } } } | ||
724 | "#####, | ||
725 | r#####" | ||
726 | fn main() { | ||
727 | let map = std::collections::HashMap::new(); | ||
728 | } | ||
729 | pub mod std { pub mod collections { pub struct HashMap { } } } | ||
730 | "#####, | ||
731 | ) | ||
732 | } | ||
733 | |||
734 | #[test] | ||
716 | fn doctest_remove_dbg() { | 735 | fn doctest_remove_dbg() { |
717 | check_doc_test( | 736 | check_doc_test( |
718 | "remove_dbg", | 737 | "remove_dbg", |
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs index 601f51098..23db3a74b 100644 --- a/crates/assists/src/utils/import_assets.rs +++ b/crates/assists/src/utils/import_assets.rs | |||
@@ -1,6 +1,4 @@ | |||
1 | //! Look up accessible paths for items. | 1 | //! Look up accessible paths for items. |
2 | use std::collections::BTreeSet; | ||
3 | |||
4 | use either::Either; | 2 | use either::Either; |
5 | use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; | 3 | use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; |
6 | use ide_db::{imports_locator, RootDatabase}; | 4 | use ide_db::{imports_locator, RootDatabase}; |
@@ -29,12 +27,12 @@ pub(crate) enum ImportCandidate { | |||
29 | #[derive(Debug)] | 27 | #[derive(Debug)] |
30 | pub(crate) struct TraitImportCandidate { | 28 | pub(crate) struct TraitImportCandidate { |
31 | pub ty: hir::Type, | 29 | pub ty: hir::Type, |
32 | pub name: String, | 30 | pub name: ast::NameRef, |
33 | } | 31 | } |
34 | 32 | ||
35 | #[derive(Debug)] | 33 | #[derive(Debug)] |
36 | pub(crate) struct PathImportCandidate { | 34 | pub(crate) struct PathImportCandidate { |
37 | pub name: String, | 35 | pub name: ast::NameRef, |
38 | } | 36 | } |
39 | 37 | ||
40 | #[derive(Debug)] | 38 | #[derive(Debug)] |
@@ -86,9 +84,9 @@ impl ImportAssets { | |||
86 | fn get_search_query(&self) -> &str { | 84 | fn get_search_query(&self) -> &str { |
87 | match &self.import_candidate { | 85 | match &self.import_candidate { |
88 | ImportCandidate::UnqualifiedName(candidate) | 86 | ImportCandidate::UnqualifiedName(candidate) |
89 | | ImportCandidate::QualifierStart(candidate) => &candidate.name, | 87 | | ImportCandidate::QualifierStart(candidate) => candidate.name.text(), |
90 | ImportCandidate::TraitAssocItem(candidate) | 88 | ImportCandidate::TraitAssocItem(candidate) |
91 | | ImportCandidate::TraitMethod(candidate) => &candidate.name, | 89 | | ImportCandidate::TraitMethod(candidate) => candidate.name.text(), |
92 | } | 90 | } |
93 | } | 91 | } |
94 | 92 | ||
@@ -96,7 +94,7 @@ impl ImportAssets { | |||
96 | &self, | 94 | &self, |
97 | sema: &Semantics<RootDatabase>, | 95 | sema: &Semantics<RootDatabase>, |
98 | config: &InsertUseConfig, | 96 | config: &InsertUseConfig, |
99 | ) -> BTreeSet<hir::ModPath> { | 97 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
100 | let _p = profile::span("import_assists::search_for_imports"); | 98 | let _p = profile::span("import_assists::search_for_imports"); |
101 | self.search_for(sema, Some(config.prefix_kind)) | 99 | self.search_for(sema, Some(config.prefix_kind)) |
102 | } | 100 | } |
@@ -106,7 +104,7 @@ impl ImportAssets { | |||
106 | pub(crate) fn search_for_relative_paths( | 104 | pub(crate) fn search_for_relative_paths( |
107 | &self, | 105 | &self, |
108 | sema: &Semantics<RootDatabase>, | 106 | sema: &Semantics<RootDatabase>, |
109 | ) -> BTreeSet<hir::ModPath> { | 107 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
110 | let _p = profile::span("import_assists::search_for_relative_paths"); | 108 | let _p = profile::span("import_assists::search_for_relative_paths"); |
111 | self.search_for(sema, None) | 109 | self.search_for(sema, None) |
112 | } | 110 | } |
@@ -115,7 +113,7 @@ impl ImportAssets { | |||
115 | &self, | 113 | &self, |
116 | sema: &Semantics<RootDatabase>, | 114 | sema: &Semantics<RootDatabase>, |
117 | prefixed: Option<hir::PrefixKind>, | 115 | prefixed: Option<hir::PrefixKind>, |
118 | ) -> BTreeSet<hir::ModPath> { | 116 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
119 | let db = sema.db; | 117 | let db = sema.db; |
120 | let mut trait_candidates = FxHashSet::default(); | 118 | let mut trait_candidates = FxHashSet::default(); |
121 | let current_crate = self.module_with_name_to_import.krate(); | 119 | let current_crate = self.module_with_name_to_import.krate(); |
@@ -181,7 +179,7 @@ impl ImportAssets { | |||
181 | } | 179 | } |
182 | }; | 180 | }; |
183 | 181 | ||
184 | imports_locator::find_imports(sema, current_crate, &self.get_search_query()) | 182 | let mut res = imports_locator::find_imports(sema, current_crate, &self.get_search_query()) |
185 | .into_iter() | 183 | .into_iter() |
186 | .filter_map(filter) | 184 | .filter_map(filter) |
187 | .filter_map(|candidate| { | 185 | .filter_map(|candidate| { |
@@ -191,10 +189,13 @@ impl ImportAssets { | |||
191 | } else { | 189 | } else { |
192 | self.module_with_name_to_import.find_use_path(db, item) | 190 | self.module_with_name_to_import.find_use_path(db, item) |
193 | } | 191 | } |
192 | .map(|path| (path, item)) | ||
194 | }) | 193 | }) |
195 | .filter(|use_path| !use_path.segments.is_empty()) | 194 | .filter(|(use_path, _)| !use_path.segments.is_empty()) |
196 | .take(20) | 195 | .take(20) |
197 | .collect::<BTreeSet<_>>() | 196 | .collect::<Vec<_>>(); |
197 | res.sort_by_key(|(path, _)| path.clone()); | ||
198 | res | ||
198 | } | 199 | } |
199 | 200 | ||
200 | fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> { | 201 | fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> { |
@@ -215,7 +216,7 @@ impl ImportCandidate { | |||
215 | Some(_) => None, | 216 | Some(_) => None, |
216 | None => Some(Self::TraitMethod(TraitImportCandidate { | 217 | None => Some(Self::TraitMethod(TraitImportCandidate { |
217 | ty: sema.type_of_expr(&method_call.receiver()?)?, | 218 | ty: sema.type_of_expr(&method_call.receiver()?)?, |
218 | name: method_call.name_ref()?.syntax().to_string(), | 219 | name: method_call.name_ref()?, |
219 | })), | 220 | })), |
220 | } | 221 | } |
221 | } | 222 | } |
@@ -243,24 +244,17 @@ impl ImportCandidate { | |||
243 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { | 244 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { |
244 | ImportCandidate::TraitAssocItem(TraitImportCandidate { | 245 | ImportCandidate::TraitAssocItem(TraitImportCandidate { |
245 | ty: assoc_item_path.ty(sema.db), | 246 | ty: assoc_item_path.ty(sema.db), |
246 | name: segment.syntax().to_string(), | 247 | name: segment.name_ref()?, |
247 | }) | 248 | }) |
248 | } | 249 | } |
249 | _ => return None, | 250 | _ => return None, |
250 | } | 251 | } |
251 | } else { | 252 | } else { |
252 | ImportCandidate::QualifierStart(PathImportCandidate { | 253 | ImportCandidate::QualifierStart(PathImportCandidate { name: qualifier_start }) |
253 | name: qualifier_start.syntax().to_string(), | ||
254 | }) | ||
255 | } | 254 | } |
256 | } else { | 255 | } else { |
257 | ImportCandidate::UnqualifiedName(PathImportCandidate { | 256 | ImportCandidate::UnqualifiedName(PathImportCandidate { |
258 | name: segment | 257 | name: segment.syntax().descendants().find_map(ast::NameRef::cast)?, |
259 | .syntax() | ||
260 | .descendants() | ||
261 | .find_map(ast::NameRef::cast)? | ||
262 | .syntax() | ||
263 | .to_string(), | ||
264 | }) | 258 | }) |
265 | }; | 259 | }; |
266 | Some(candidate) | 260 | Some(candidate) |