aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/nameres.rs4
-rw-r--r--crates/ra_hir/src/nameres/collector.rs31
-rw-r--r--crates/ra_hir/src/nameres/raw.rs22
-rw-r--r--crates/ra_hir/src/nameres/tests.rs97
-rw-r--r--crates/ra_hir/src/nameres/tests/mods.rs192
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs2
-rw-r--r--crates/ra_ide_api/src/completion/complete_postfix.rs82
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__postfix_completion_works_for_trivial_path_expression.snap64
-rw-r--r--crates/ra_prof/src/lib.rs2
9 files changed, 312 insertions, 184 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index 53ef8d58a..c84d2eada 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -1,6 +1,6 @@
1/// This module implements import-resolution/macro expansion algorithm. 1/// This module implements import-resolution/macro expansion algorithm.
2/// 2///
3/// The result of this module is `CrateDefMap`: a datastructure which contains: 3/// The result of this module is `CrateDefMap`: a data structure which contains:
4/// 4///
5/// * a tree of modules for the crate 5/// * a tree of modules for the crate
6/// * for each module, a set of items visible in the module (directly declared 6/// * for each module, a set of items visible in the module (directly declared
@@ -76,7 +76,7 @@ pub use self::{
76 raw::ImportId, 76 raw::ImportId,
77}; 77};
78 78
79/// Contans all top-level defs from a macro-expanded crate 79/// Contains all top-level defs from a macro-expanded crate
80#[derive(Debug, PartialEq, Eq)] 80#[derive(Debug, PartialEq, Eq)]
81pub struct CrateDefMap { 81pub struct CrateDefMap {
82 krate: Crate, 82 krate: Crate,
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
index d66be34db..9f197bb58 100644
--- a/crates/ra_hir/src/nameres/collector.rs
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -1,6 +1,6 @@
1use arrayvec::ArrayVec; 1use arrayvec::ArrayVec;
2use ra_db::FileId; 2use ra_db::FileId;
3use ra_syntax::ast; 3use ra_syntax::{ast, SmolStr};
4use relative_path::RelativePathBuf; 4use relative_path::RelativePathBuf;
5use rustc_hash::FxHashMap; 5use rustc_hash::FxHashMap;
6use test_utils::tested_by; 6use test_utils::tested_by;
@@ -508,11 +508,17 @@ where
508 } 508 }
509 .collect(&*items); 509 .collect(&*items);
510 } 510 }
511 // out of line module, resovle, parse and recurse 511 // out of line module, resolve, parse and recurse
512 raw::ModuleData::Declaration { name, ast_id } => { 512 raw::ModuleData::Declaration { name, ast_id, attr_path } => {
513 let ast_id = ast_id.with_file_id(self.file_id); 513 let ast_id = ast_id.with_file_id(self.file_id);
514 let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); 514 let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none();
515 match resolve_submodule(self.def_collector.db, self.file_id, name, is_root) { 515 match resolve_submodule(
516 self.def_collector.db,
517 self.file_id,
518 name,
519 is_root,
520 attr_path.as_ref(),
521 ) {
516 Ok(file_id) => { 522 Ok(file_id) => {
517 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); 523 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id));
518 let raw_items = self.def_collector.db.raw_items(file_id.into()); 524 let raw_items = self.def_collector.db.raw_items(file_id.into());
@@ -626,6 +632,7 @@ fn resolve_submodule(
626 file_id: HirFileId, 632 file_id: HirFileId,
627 name: &Name, 633 name: &Name,
628 is_root: bool, 634 is_root: bool,
635 attr_path: Option<&SmolStr>,
629) -> Result<FileId, RelativePathBuf> { 636) -> Result<FileId, RelativePathBuf> {
630 // FIXME: handle submodules of inline modules properly 637 // FIXME: handle submodules of inline modules properly
631 let file_id = file_id.original_file(db); 638 let file_id = file_id.original_file(db);
@@ -639,7 +646,13 @@ fn resolve_submodule(
639 let file_mod = dir_path.join(format!("{}.rs", name)); 646 let file_mod = dir_path.join(format!("{}.rs", name));
640 let dir_mod = dir_path.join(format!("{}/mod.rs", name)); 647 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
641 let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name)); 648 let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
642 let mut candidates = ArrayVec::<[_; 2]>::new(); 649 let mut candidates = ArrayVec::<[_; 3]>::new();
650 let file_attr_mod = attr_path.map(|file_path| {
651 let file_attr_mod = dir_path.join(file_path.to_string());
652 candidates.push(file_attr_mod.clone());
653
654 file_attr_mod
655 });
643 if is_dir_owner { 656 if is_dir_owner {
644 candidates.push(file_mod.clone()); 657 candidates.push(file_mod.clone());
645 candidates.push(dir_mod); 658 candidates.push(dir_mod);
@@ -651,7 +664,13 @@ fn resolve_submodule(
651 // FIXME: handle ambiguity 664 // FIXME: handle ambiguity
652 match points_to.next() { 665 match points_to.next() {
653 Some(file_id) => Ok(file_id), 666 Some(file_id) => Ok(file_id),
654 None => Err(if is_dir_owner { file_mod } else { file_dir_mod }), 667 None => {
668 if let Some(file_attr_mod) = file_attr_mod {
669 Err(file_attr_mod)
670 } else {
671 Err(if is_dir_owner { file_mod } else { file_dir_mod })
672 }
673 }
655 } 674 }
656} 675}
657 676
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs
index 7ea59cb75..46b2bef5b 100644
--- a/crates/ra_hir/src/nameres/raw.rs
+++ b/crates/ra_hir/src/nameres/raw.rs
@@ -3,7 +3,7 @@ use std::{ops::Index, sync::Arc};
3use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; 3use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, AttrsOwner, NameOwner}, 5 ast::{self, AttrsOwner, NameOwner},
6 AstNode, AstPtr, SourceFile, TreeArc, 6 AstNode, AstPtr, SmolStr, SourceFile, TreeArc,
7}; 7};
8use test_utils::tested_by; 8use test_utils::tested_by;
9 9
@@ -130,7 +130,7 @@ impl_arena_id!(Module);
130 130
131#[derive(Debug, PartialEq, Eq)] 131#[derive(Debug, PartialEq, Eq)]
132pub(super) enum ModuleData { 132pub(super) enum ModuleData {
133 Declaration { name: Name, ast_id: FileAstId<ast::Module> }, 133 Declaration { name: Name, ast_id: FileAstId<ast::Module>, attr_path: Option<SmolStr> },
134 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> }, 134 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
135} 135}
136 136
@@ -255,9 +255,12 @@ impl RawItemsCollector {
255 Some(it) => it.as_name(), 255 Some(it) => it.as_name(),
256 None => return, 256 None => return,
257 }; 257 };
258
259 let attr_path = extract_mod_path_attribute(module);
258 let ast_id = self.source_ast_id_map.ast_id(module); 260 let ast_id = self.source_ast_id_map.ast_id(module);
259 if module.has_semi() { 261 if module.has_semi() {
260 let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id }); 262 let item =
263 self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path });
261 self.push_item(current_module, RawItem::Module(item)); 264 self.push_item(current_module, RawItem::Module(item));
262 return; 265 return;
263 } 266 }
@@ -339,3 +342,16 @@ impl RawItemsCollector {
339 .push(item) 342 .push(item)
340 } 343 }
341} 344}
345
346fn extract_mod_path_attribute(module: &ast::Module) -> Option<SmolStr> {
347 module.attrs().into_iter().find_map(|attr| {
348 attr.as_key_value().and_then(|(name, value)| {
349 let is_path = name == "path";
350 if is_path {
351 Some(value)
352 } else {
353 None
354 }
355 })
356 })
357}
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index bd2d855cf..8b0887fb5 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -2,6 +2,7 @@ mod macros;
2mod globs; 2mod globs;
3mod incremental; 3mod incremental;
4mod primitives; 4mod primitives;
5mod mods;
5 6
6use std::sync::Arc; 7use std::sync::Arc;
7 8
@@ -313,83 +314,6 @@ fn edition_2015_imports() {
313} 314}
314 315
315#[test] 316#[test]
316fn module_resolution_works_for_non_standard_filenames() {
317 let map = def_map_with_crate_graph(
318 "
319 //- /my_library.rs
320 mod foo;
321 use self::foo::Bar;
322
323 //- /foo/mod.rs
324 pub struct Bar;
325 ",
326 crate_graph! {
327 "my_library": ("/my_library.rs", []),
328 },
329 );
330
331 assert_snapshot_matches!(map, @r###"
332 ⋮crate
333 ⋮Bar: t v
334 ⋮foo: t
335
336 ⋮crate::foo
337 ⋮Bar: t v
338 "###);
339}
340
341#[test]
342fn module_resolution_works_for_raw_modules() {
343 let map = def_map_with_crate_graph(
344 "
345 //- /library.rs
346 mod r#async;
347 use self::r#async::Bar;
348
349 //- /async.rs
350 pub struct Bar;
351 ",
352 crate_graph! {
353 "library": ("/library.rs", []),
354 },
355 );
356
357 assert_snapshot_matches!(map, @r###"
358 ⋮crate
359 ⋮Bar: t v
360 ⋮async: t
361
362 ⋮crate::async
363 ⋮Bar: t v
364 "###);
365}
366
367#[test]
368fn name_res_works_for_broken_modules() {
369 covers!(name_res_works_for_broken_modules);
370 let map = def_map(
371 "
372 //- /lib.rs
373 mod foo // no `;`, no body
374
375 use self::foo::Baz;
376
377 //- /foo/mod.rs
378 pub mod bar;
379
380 pub use self::bar::Baz;
381
382 //- /foo/bar.rs
383 pub struct Baz;
384 ",
385 );
386 assert_snapshot_matches!(map, @r###"
387 ⋮crate
388 ⋮Baz: _
389 "###);
390}
391
392#[test]
393fn item_map_using_self() { 317fn item_map_using_self() {
394 let map = def_map( 318 let map = def_map(
395 " 319 "
@@ -581,22 +505,3 @@ fn values_dont_shadow_extern_crates() {
581 ⋮foo: v 505 ⋮foo: v
582 "###); 506 "###);
583} 507}
584
585#[test]
586fn unresolved_module_diagnostics() {
587 let diagnostics = MockDatabase::with_files(
588 r"
589 //- /lib.rs
590 mod foo;
591 mod bar;
592 mod baz {}
593 //- /foo.rs
594 ",
595 )
596 .diagnostics();
597
598 assert_snapshot_matches!(diagnostics, @r###"
599"mod bar;": unresolved module
600"###
601 );
602}
diff --git a/crates/ra_hir/src/nameres/tests/mods.rs b/crates/ra_hir/src/nameres/tests/mods.rs
new file mode 100644
index 000000000..7c8c832fc
--- /dev/null
+++ b/crates/ra_hir/src/nameres/tests/mods.rs
@@ -0,0 +1,192 @@
1use super::*;
2
3#[test]
4fn name_res_works_for_broken_modules() {
5 covers!(name_res_works_for_broken_modules);
6 let map = def_map(
7 "
8 //- /lib.rs
9 mod foo // no `;`, no body
10
11 use self::foo::Baz;
12
13 //- /foo/mod.rs
14 pub mod bar;
15
16 pub use self::bar::Baz;
17
18 //- /foo/bar.rs
19 pub struct Baz;
20 ",
21 );
22 assert_snapshot_matches!(map, @r###"
23 ⋮crate
24 ⋮Baz: _
25 "###);
26}
27
28#[test]
29fn module_resolution_works_for_non_standard_filenames() {
30 let map = def_map_with_crate_graph(
31 "
32 //- /my_library.rs
33 mod foo;
34 use self::foo::Bar;
35
36 //- /foo/mod.rs
37 pub struct Bar;
38 ",
39 crate_graph! {
40 "my_library": ("/my_library.rs", []),
41 },
42 );
43
44 assert_snapshot_matches!(map, @r###"
45 ⋮crate
46 ⋮Bar: t v
47 ⋮foo: t
48
49 ⋮crate::foo
50 ⋮Bar: t v
51 "###);
52}
53
54#[test]
55fn module_resolution_works_for_raw_modules() {
56 let map = def_map_with_crate_graph(
57 "
58 //- /library.rs
59 mod r#async;
60 use self::r#async::Bar;
61
62 //- /async.rs
63 pub struct Bar;
64 ",
65 crate_graph! {
66 "library": ("/library.rs", []),
67 },
68 );
69
70 assert_snapshot_matches!(map, @r###"
71 ⋮crate
72 ⋮Bar: t v
73 ⋮async: t
74
75 ⋮crate::async
76 ⋮Bar: t v
77 "###);
78}
79
80#[test]
81fn module_resolution_decl_path() {
82 let map = def_map_with_crate_graph(
83 "
84 //- /library.rs
85 #[path = \"bar/baz/foo.rs\"]
86 mod foo;
87 use self::foo::Bar;
88
89 //- /bar/baz/foo.rs
90 pub struct Bar;
91 ",
92 crate_graph! {
93 "library": ("/library.rs", []),
94 },
95 );
96
97 assert_snapshot_matches!(map, @r###"
98 ⋮crate
99 ⋮Bar: t v
100 ⋮foo: t
101
102 ⋮crate::foo
103 ⋮Bar: t v
104 "###);
105}
106
107#[test]
108fn module_resolution_module_with_path_in_mod_rs() {
109 let map = def_map_with_crate_graph(
110 "
111 //- /main.rs
112 mod foo;
113
114 //- /foo/mod.rs
115 #[path = \"baz.rs\"]
116 pub mod bar;
117
118 use self::bar::Baz;
119
120 //- /foo/baz.rs
121 pub struct Baz;
122 ",
123 crate_graph! {
124 "main": ("/main.rs", []),
125 },
126 );
127
128 assert_snapshot_matches!(map, @r###"
129 ⋮crate
130 ⋮foo: t
131
132 ⋮crate::foo
133 ⋮Baz: t v
134 ⋮bar: t
135
136 ⋮crate::foo::bar
137 ⋮Baz: t v
138 "###);
139}
140
141#[test]
142fn module_resolution_module_with_path_non_crate_root() {
143 let map = def_map_with_crate_graph(
144 "
145 //- /main.rs
146 mod foo;
147
148 //- /foo.rs
149 #[path = \"baz.rs\"]
150 pub mod bar;
151
152 use self::bar::Baz;
153
154 //- /baz.rs
155 pub struct Baz;
156 ",
157 crate_graph! {
158 "main": ("/main.rs", []),
159 },
160 );
161
162 assert_snapshot_matches!(map, @r###"
163 ⋮crate
164 ⋮foo: t
165
166 ⋮crate::foo
167 ⋮Baz: t v
168 ⋮bar: t
169
170 ⋮crate::foo::bar
171 ⋮Baz: t v
172 "###);
173}
174
175#[test]
176fn unresolved_module_diagnostics() {
177 let diagnostics = MockDatabase::with_files(
178 r"
179 //- /lib.rs
180 mod foo;
181 mod bar;
182 mod baz {}
183 //- /foo.rs
184 ",
185 )
186 .diagnostics();
187
188 assert_snapshot_matches!(diagnostics, @r###"
189"mod bar;": unresolved module
190"###
191 );
192}
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs
index 10a6e0b10..76ace66ea 100644
--- a/crates/ra_hir/src/ty/method_resolution.rs
+++ b/crates/ra_hir/src/ty/method_resolution.rs
@@ -116,7 +116,7 @@ impl CrateImplBlocks {
116 116
117fn def_crates(db: &impl HirDatabase, cur_crate: Crate, ty: &Ty) -> Option<ArrayVec<[Crate; 2]>> { 117fn def_crates(db: &impl HirDatabase, cur_crate: Crate, ty: &Ty) -> Option<ArrayVec<[Crate; 2]>> {
118 // Types like slice can have inherent impls in several crates, (core and alloc). 118 // Types like slice can have inherent impls in several crates, (core and alloc).
119 // The correspoinding impls are marked with lang items, so we can use them to find the required crates. 119 // The corresponding impls are marked with lang items, so we can use them to find the required crates.
120 macro_rules! lang_item_crate { 120 macro_rules! lang_item_crate {
121 ($db:expr, $cur_crate:expr, $($name:expr),+ $(,)?) => {{ 121 ($db:expr, $cur_crate:expr, $($name:expr),+ $(,)?) => {{
122 let mut v = ArrayVec::<[Crate; 2]>::new(); 122 let mut v = ArrayVec::<[Crate; 2]>::new();
diff --git a/crates/ra_ide_api/src/completion/complete_postfix.rs b/crates/ra_ide_api/src/completion/complete_postfix.rs
index 7042d8bff..a25e517f5 100644
--- a/crates/ra_ide_api/src/completion/complete_postfix.rs
+++ b/crates/ra_ide_api/src/completion/complete_postfix.rs
@@ -51,22 +51,82 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
51 51
52#[cfg(test)] 52#[cfg(test)]
53mod tests { 53mod tests {
54 use crate::completion::{check_completion, CompletionKind}; 54 use crate::completion::{do_completion, CompletionItem, CompletionKind};
55 use insta::assert_debug_snapshot_matches;
55 56
56 fn check_snippet_completion(test_name: &str, code: &str) { 57 fn do_postfix_completion(code: &str) -> Vec<CompletionItem> {
57 check_completion(test_name, code, CompletionKind::Postfix); 58 do_completion(code, CompletionKind::Postfix)
58 } 59 }
59 60
60 #[test] 61 #[test]
61 fn postfix_completion_works_for_trivial_path_expression() { 62 fn postfix_completion_works_for_trivial_path_expression() {
62 check_snippet_completion( 63 assert_debug_snapshot_matches!(
63 "postfix_completion_works_for_trivial_path_expression", 64 do_postfix_completion(
64 r#" 65 r#"
65 fn main() { 66 fn main() {
66 let bar = "a"; 67 let bar = "a";
67 bar.<|> 68 bar.<|>
68 } 69 }
69 "#, 70 "#,
71 ),
72 @r###"[
73 CompletionItem {
74 label: "box",
75 source_range: [88; 88),
76 delete: [84; 88),
77 insert: "Box::new(bar)",
78 detail: "Box::new(expr)",
79 },
80 CompletionItem {
81 label: "dbg",
82 source_range: [88; 88),
83 delete: [84; 88),
84 insert: "dbg!(bar)",
85 detail: "dbg!(expr)",
86 },
87 CompletionItem {
88 label: "if",
89 source_range: [88; 88),
90 delete: [84; 88),
91 insert: "if bar {$0}",
92 detail: "if expr {}",
93 },
94 CompletionItem {
95 label: "match",
96 source_range: [88; 88),
97 delete: [84; 88),
98 insert: "match bar {\n ${1:_} => {$0\\},\n}",
99 detail: "match expr {}",
100 },
101 CompletionItem {
102 label: "not",
103 source_range: [88; 88),
104 delete: [84; 88),
105 insert: "!bar",
106 detail: "!expr",
107 },
108 CompletionItem {
109 label: "ref",
110 source_range: [88; 88),
111 delete: [84; 88),
112 insert: "&bar",
113 detail: "&expr",
114 },
115 CompletionItem {
116 label: "refm",
117 source_range: [88; 88),
118 delete: [84; 88),
119 insert: "&mut bar",
120 detail: "&mut expr",
121 },
122 CompletionItem {
123 label: "while",
124 source_range: [88; 88),
125 delete: [84; 88),
126 insert: "while bar {\n$0\n}",
127 detail: "while expr {}",
128 },
129]"###
70 ); 130 );
71 } 131 }
72} 132}
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__postfix_completion_works_for_trivial_path_expression.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__postfix_completion_works_for_trivial_path_expression.snap
deleted file mode 100644
index c1a40b7b4..000000000
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__postfix_completion_works_for_trivial_path_expression.snap
+++ /dev/null
@@ -1,64 +0,0 @@
1---
2created: "2019-06-23T13:01:08.775536006Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions
6---
7[
8 CompletionItem {
9 label: "box",
10 source_range: [76; 76),
11 delete: [72; 76),
12 insert: "Box::new(bar)",
13 detail: "Box::new(expr)",
14 },
15 CompletionItem {
16 label: "dbg",
17 source_range: [76; 76),
18 delete: [72; 76),
19 insert: "dbg!(bar)",
20 detail: "dbg!(expr)",
21 },
22 CompletionItem {
23 label: "if",
24 source_range: [76; 76),
25 delete: [72; 76),
26 insert: "if bar {$0}",
27 detail: "if expr {}",
28 },
29 CompletionItem {
30 label: "match",
31 source_range: [76; 76),
32 delete: [72; 76),
33 insert: "match bar {\n ${1:_} => {$0\\},\n}",
34 detail: "match expr {}",
35 },
36 CompletionItem {
37 label: "not",
38 source_range: [76; 76),
39 delete: [72; 76),
40 insert: "!bar",
41 detail: "!expr",
42 },
43 CompletionItem {
44 label: "ref",
45 source_range: [76; 76),
46 delete: [72; 76),
47 insert: "&bar",
48 detail: "&expr",
49 },
50 CompletionItem {
51 label: "refm",
52 source_range: [76; 76),
53 delete: [72; 76),
54 insert: "&mut bar",
55 detail: "&mut expr",
56 },
57 CompletionItem {
58 label: "while",
59 source_range: [76; 76),
60 delete: [72; 76),
61 insert: "while bar {\n$0\n}",
62 detail: "while expr {}",
63 },
64]
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 919cc1b3c..6d44fef33 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -52,7 +52,7 @@ pub fn set_filter(f: Filter) {
52/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output. 52/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output.
53/// Profiling information is being printed in the stderr. 53/// Profiling information is being printed in the stderr.
54/// 54///
55/// #Example 55/// # Example
56/// ``` 56/// ```
57/// use ra_prof::{profile, set_filter, Filter}; 57/// use ra_prof::{profile, set_filter, Filter};
58/// 58///