ide_assists/handlers/
remove_unused_imports.rs

1use std::collections::hash_map::Entry;
2
3use hir::{
4    FileRange, InFile, InRealFile, Module, ModuleDef, ModuleSource, PathResolution,
5    PathResolutionPerNs,
6};
7use ide_db::text_edit::TextRange;
8use ide_db::{
9    FxHashMap, RootDatabase,
10    defs::Definition,
11    search::{FileReference, ReferenceCategory, SearchScope},
12};
13use syntax::{
14    AstNode,
15    ast::{self, Rename},
16};
17
18use crate::{AssistContext, AssistId, Assists};
19
20// Assist: remove_unused_imports
21//
22// Removes any use statements in the current selection that are unused.
23//
24// ```
25// struct X();
26// mod foo {
27//     use super::X$0;
28// }
29// ```
30// ->
31// ```
32// struct X();
33// mod foo {
34// }
35// ```
36pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
37    // First, grab the uses that intersect with the current selection.
38    let selected_el = match ctx.covering_element() {
39        syntax::NodeOrToken::Node(n) => n,
40        syntax::NodeOrToken::Token(t) => t.parent()?,
41    };
42
43    // This applies to all uses that are selected, or are ancestors of our selection.
44    let uses_up = selected_el.ancestors().skip(1).filter_map(ast::Use::cast);
45    let uses_down = selected_el
46        .descendants()
47        .filter(|x| x.text_range().intersect(ctx.selection_trimmed()).is_some())
48        .filter_map(ast::Use::cast);
49    let uses = uses_up.chain(uses_down).collect::<Vec<_>>();
50
51    // Maps use nodes to the scope that we should search through to find
52    let mut search_scopes = FxHashMap::<Module, Vec<SearchScope>>::default();
53
54    // iterator over all unused use trees
55    let mut unused = uses
56        .into_iter()
57        .flat_map(|u| u.syntax().descendants().filter_map(ast::UseTree::cast))
58        .filter(|u| u.use_tree_list().is_none())
59        .filter_map(|u| {
60            // Find any uses trees that are unused
61
62            let use_module = ctx.sema.scope(u.syntax()).map(|s| s.module())?;
63            let scope = match search_scopes.entry(use_module) {
64                Entry::Occupied(o) => o.into_mut(),
65                Entry::Vacant(v) => v.insert(module_search_scope(ctx.db(), use_module)),
66            };
67
68            // Gets the path associated with this use tree. If there isn't one, then ignore this use tree.
69            let path = if let Some(path) = u.path() {
70                path
71            } else if u.star_token().is_some() {
72                // This case maps to the situation where the * token is braced.
73                // In this case, the parent use tree's path is the one we should use to resolve the glob.
74                match u.syntax().ancestors().skip(1).find_map(ast::UseTree::cast) {
75                    Some(parent_u) if parent_u.path().is_some() => parent_u.path()?,
76                    _ => return None,
77                }
78            } else {
79                return None;
80            };
81
82            // Get the actual definition associated with this use item.
83            let res = match ctx.sema.resolve_path_per_ns(&path) {
84                Some(x) if x.any().is_some() => x,
85                Some(_) | None => {
86                    return None;
87                }
88            };
89
90            if u.star_token().is_some() {
91                // Check if any of the children of this module are used
92                let def_mod = match res.type_ns {
93                    Some(PathResolution::Def(ModuleDef::Module(module))) => module,
94                    _ => return None,
95                };
96
97                if !def_mod
98                    .scope(ctx.db(), Some(use_module))
99                    .iter()
100                    .filter_map(|(_, x)| match x {
101                        hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)),
102                        _ => None,
103                    })
104                    .any(|d| used_once_in_scope(ctx, d, u.rename(), scope))
105                {
106                    Some(u)
107                } else {
108                    None
109                }
110            } else {
111                is_path_per_ns_unused_in_scope(ctx, &u, scope, &res).then_some(u)
112            }
113        })
114        .peekable();
115
116    // Peek so we terminate early if an unused use is found. Only do the rest of the work if the user selects the assist.
117    if unused.peek().is_some() {
118        acc.add(
119            AssistId::quick_fix("remove_unused_imports"),
120            "Remove all unused imports",
121            selected_el.text_range(),
122            |builder| {
123                let unused: Vec<ast::UseTree> = unused.map(|x| builder.make_mut(x)).collect();
124                for node in unused {
125                    node.remove_recursive();
126                }
127            },
128        )
129    } else {
130        None
131    }
132}
133
134fn is_path_per_ns_unused_in_scope(
135    ctx: &AssistContext<'_>,
136    u: &ast::UseTree,
137    scope: &mut Vec<SearchScope>,
138    path: &PathResolutionPerNs,
139) -> bool {
140    if let Some(PathResolution::Def(ModuleDef::Trait(ref t))) = path.type_ns {
141        if is_trait_unused_in_scope(ctx, u, scope, t) {
142            let path = [path.value_ns, path.macro_ns];
143            is_path_unused_in_scope(ctx, u, scope, &path)
144        } else {
145            false
146        }
147    } else {
148        let path = [path.type_ns, path.value_ns, path.macro_ns];
149        is_path_unused_in_scope(ctx, u, scope, &path)
150    }
151}
152
153fn is_path_unused_in_scope(
154    ctx: &AssistContext<'_>,
155    u: &ast::UseTree,
156    scope: &mut Vec<SearchScope>,
157    path: &[Option<PathResolution>],
158) -> bool {
159    !path
160        .iter()
161        .filter_map(|path| *path)
162        .filter_map(|res| match res {
163            PathResolution::Def(d) => Some(Definition::from(d)),
164            _ => None,
165        })
166        .any(|def| used_once_in_scope(ctx, def, u.rename(), scope))
167}
168
169fn is_trait_unused_in_scope(
170    ctx: &AssistContext<'_>,
171    u: &ast::UseTree,
172    scope: &mut Vec<SearchScope>,
173    t: &hir::Trait,
174) -> bool {
175    !std::iter::once((Definition::Trait(*t), u.rename()))
176        .chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None)))
177        .any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope))
178}
179
180fn used_once_in_scope(
181    ctx: &AssistContext<'_>,
182    def: Definition,
183    rename: Option<Rename>,
184    scopes: &Vec<SearchScope>,
185) -> bool {
186    let mut found = false;
187
188    for scope in scopes {
189        let mut search_non_import = |_, r: FileReference| {
190            // The import itself is a use; we must skip that.
191            if !r.category.contains(ReferenceCategory::IMPORT) {
192                found = true;
193                true
194            } else {
195                false
196            }
197        };
198        def.usages(&ctx.sema)
199            .in_scope(scope)
200            .with_rename(rename.as_ref())
201            .search(&mut search_non_import);
202        if found {
203            break;
204        }
205    }
206
207    found
208}
209
210/// Build a search scope spanning the given module but none of its submodules.
211fn module_search_scope(db: &RootDatabase, module: hir::Module) -> Vec<SearchScope> {
212    let (file_id, range) = {
213        let InFile { file_id, value } = module.definition_source(db);
214        if let Some(InRealFile { file_id, value: call_source }) = file_id.original_call_node(db) {
215            (file_id, Some(call_source.text_range()))
216        } else {
217            (
218                file_id.original_file(db),
219                match value {
220                    ModuleSource::SourceFile(_) => None,
221                    ModuleSource::Module(it) => Some(it.syntax().text_range()),
222                    ModuleSource::BlockExpr(it) => Some(it.syntax().text_range()),
223                },
224            )
225        }
226    };
227
228    fn split_at_subrange(first: TextRange, second: TextRange) -> (TextRange, Option<TextRange>) {
229        let intersect = first.intersect(second);
230        if let Some(intersect) = intersect {
231            let start_range = TextRange::new(first.start(), intersect.start());
232
233            if intersect.end() < first.end() {
234                (start_range, Some(TextRange::new(intersect.end(), first.end())))
235            } else {
236                (start_range, None)
237            }
238        } else {
239            (first, None)
240        }
241    }
242
243    let mut scopes = Vec::new();
244    if let Some(range) = range {
245        let mut ranges = vec![range];
246
247        for child in module.children(db) {
248            let rng = match child.definition_source(db).value {
249                ModuleSource::SourceFile(_) => continue,
250                ModuleSource::Module(it) => it.syntax().text_range(),
251                ModuleSource::BlockExpr(_) => continue,
252            };
253            let mut new_ranges = Vec::new();
254            for old_range in ranges.iter_mut() {
255                let split = split_at_subrange(*old_range, rng);
256                *old_range = split.0;
257                new_ranges.extend(split.1);
258            }
259
260            ranges.append(&mut new_ranges);
261        }
262
263        for range in ranges {
264            scopes.push(SearchScope::file_range(FileRange { file_id, range }));
265        }
266    } else {
267        scopes.push(SearchScope::single_file(file_id));
268    }
269
270    scopes
271}
272
273#[cfg(test)]
274mod tests {
275    use crate::tests::{check_assist, check_assist_not_applicable};
276
277    use super::*;
278
279    #[test]
280    fn remove_unused() {
281        check_assist(
282            remove_unused_imports,
283            r#"
284struct X();
285struct Y();
286mod z {
287    $0use super::X;
288    use super::Y;$0
289}
290"#,
291            r#"
292struct X();
293struct Y();
294mod z {
295}
296"#,
297        );
298    }
299
300    #[test]
301    fn remove_unused_is_precise() {
302        check_assist(
303            remove_unused_imports,
304            r#"
305struct X();
306mod z {
307$0use super::X;$0
308
309fn w() {
310    struct X();
311    let x = X();
312}
313}
314"#,
315            r#"
316struct X();
317mod z {
318
319fn w() {
320    struct X();
321    let x = X();
322}
323}
324"#,
325        );
326    }
327
328    #[test]
329    fn trait_name_use_is_use() {
330        check_assist_not_applicable(
331            remove_unused_imports,
332            r#"
333struct X();
334trait Y {
335    fn f();
336}
337
338impl Y for X {
339    fn f() {}
340}
341mod z {
342$0use super::X;
343use super::Y;$0
344
345fn w() {
346    X::f();
347}
348}
349"#,
350        );
351    }
352
353    #[test]
354    fn trait_item_use_is_use() {
355        check_assist_not_applicable(
356            remove_unused_imports,
357            r#"
358struct X();
359trait Y {
360    fn f(self);
361}
362
363impl Y for X {
364    fn f(self) {}
365}
366mod z {
367$0use super::X;
368use super::Y;$0
369
370fn w() {
371    let x = X();
372    x.f();
373}
374}
375"#,
376        );
377    }
378
379    #[test]
380    fn renamed_trait_item_use_is_use() {
381        check_assist_not_applicable(
382            remove_unused_imports,
383            r#"
384struct X();
385trait Y {
386    fn f(self);
387}
388
389impl Y for X {
390    fn f(self) {}
391}
392mod z {
393$0use super::X;
394use super::Y as Z;$0
395
396fn w() {
397    let x = X();
398    x.f();
399}
400}
401"#,
402        );
403    }
404
405    #[test]
406    fn renamed_underscore_trait_item_use_is_use() {
407        check_assist_not_applicable(
408            remove_unused_imports,
409            r#"
410struct X();
411trait Y {
412    fn f(self);
413}
414
415impl Y for X {
416    fn f(self) {}
417}
418mod z {
419$0use super::X;
420use super::Y as _;$0
421
422fn w() {
423    let x = X();
424    x.f();
425}
426}
427"#,
428        );
429    }
430
431    #[test]
432    fn dont_remove_used() {
433        check_assist_not_applicable(
434            remove_unused_imports,
435            r#"
436struct X();
437struct Y();
438mod z {
439$0use super::X;
440use super::Y;$0
441
442fn w() {
443    let x = X();
444    let y = Y();
445}
446}
447"#,
448        );
449    }
450
451    #[test]
452    fn remove_unused_in_braces() {
453        check_assist(
454            remove_unused_imports,
455            r#"
456struct X();
457struct Y();
458mod z {
459    $0use super::{X, Y};$0
460
461    fn w() {
462        let x = X();
463    }
464}
465"#,
466            r#"
467struct X();
468struct Y();
469mod z {
470    use super::X;
471
472    fn w() {
473        let x = X();
474    }
475}
476"#,
477        );
478    }
479
480    #[test]
481    fn remove_unused_under_cursor() {
482        check_assist(
483            remove_unused_imports,
484            r#"
485struct X();
486mod z {
487    use super::X$0;
488}
489"#,
490            r#"
491struct X();
492mod z {
493}
494"#,
495        );
496    }
497
498    #[test]
499    fn remove_multi_use_block() {
500        check_assist(
501            remove_unused_imports,
502            r#"
503struct X();
504$0mod y {
505    use super::X;
506}
507mod z {
508    use super::X;
509}$0
510"#,
511            r#"
512struct X();
513mod y {
514}
515mod z {
516}
517"#,
518        );
519    }
520
521    #[test]
522    fn remove_nested() {
523        check_assist(
524            remove_unused_imports,
525            r#"
526struct X();
527mod y {
528    struct Y();
529    mod z {
530        use crate::{X, y::Y}$0;
531        fn f() {
532            let x = X();
533        }
534    }
535}
536"#,
537            r#"
538struct X();
539mod y {
540    struct Y();
541    mod z {
542        use crate::X;
543        fn f() {
544            let x = X();
545        }
546    }
547}
548"#,
549        );
550    }
551
552    #[test]
553    fn remove_nested_first_item() {
554        check_assist(
555            remove_unused_imports,
556            r#"
557struct X();
558mod y {
559    struct Y();
560    mod z {
561        use crate::{X, y::Y}$0;
562        fn f() {
563            let y = Y();
564        }
565    }
566}
567"#,
568            r#"
569struct X();
570mod y {
571    struct Y();
572    mod z {
573        use crate::y::Y;
574        fn f() {
575            let y = Y();
576        }
577    }
578}
579"#,
580        );
581    }
582
583    #[test]
584    fn remove_unused_auto_remove_brace_nested() {
585        check_assist(
586            remove_unused_imports,
587            r#"
588mod a {
589    pub struct A();
590}
591mod b {
592    struct F();
593    mod c {
594        $0use {{super::{{
595            {d::{{{{{{{S, U}}}}}}}},
596            {{{{e::{H, L, {{{R}}}}}}}},
597            F, super::a::A
598        }}}};$0
599        fn f() {
600            let f = F();
601            let l = L();
602            let a = A();
603            let s = S();
604            let h = H();
605        }
606    }
607
608    mod d {
609        pub struct S();
610        pub struct U();
611    }
612
613    mod e {
614        pub struct H();
615        pub struct L();
616        pub struct R();
617    }
618}
619"#,
620            r#"
621mod a {
622    pub struct A();
623}
624mod b {
625    struct F();
626    mod c {
627        use super::{
628            d::S,
629            e::{H, L},
630            F, super::a::A
631        };
632        fn f() {
633            let f = F();
634            let l = L();
635            let a = A();
636            let s = S();
637            let h = H();
638        }
639    }
640
641    mod d {
642        pub struct S();
643        pub struct U();
644    }
645
646    mod e {
647        pub struct H();
648        pub struct L();
649        pub struct R();
650    }
651}
652"#,
653        );
654    }
655
656    #[test]
657    fn remove_comma_after_auto_remove_brace() {
658        check_assist(
659            remove_unused_imports,
660            r#"
661mod m {
662    pub mod x {
663        pub struct A;
664        pub struct B;
665    }
666    pub mod y {
667        pub struct C;
668    }
669}
670
671$0use m::{
672    x::{A, B},
673    y::C,
674};$0
675
676fn main() {
677    B;
678}
679"#,
680            r#"
681mod m {
682    pub mod x {
683        pub struct A;
684        pub struct B;
685    }
686    pub mod y {
687        pub struct C;
688    }
689}
690
691use m::
692    x::B
693;
694
695fn main() {
696    B;
697}
698"#,
699        );
700        check_assist(
701            remove_unused_imports,
702            r#"
703mod m {
704    pub mod x {
705        pub struct A;
706        pub struct B;
707    }
708    pub mod y {
709        pub struct C;
710        pub struct D;
711    }
712    pub mod z {
713        pub struct E;
714        pub struct F;
715    }
716}
717
718$0use m::{
719    x::{A, B},
720    y::{C, D,},
721    z::{E, F},
722};$0
723
724fn main() {
725    B;
726    C;
727    F;
728}
729"#,
730            r#"
731mod m {
732    pub mod x {
733        pub struct A;
734        pub struct B;
735    }
736    pub mod y {
737        pub struct C;
738        pub struct D;
739    }
740    pub mod z {
741        pub struct E;
742        pub struct F;
743    }
744}
745
746use m::{
747    x::B,
748    y::C,
749    z::F,
750};
751
752fn main() {
753    B;
754    C;
755    F;
756}
757"#,
758        );
759    }
760
761    #[test]
762    fn remove_nested_all_unused() {
763        check_assist(
764            remove_unused_imports,
765            r#"
766struct X();
767mod y {
768    struct Y();
769    mod z {
770        use crate::{X, y::Y}$0;
771    }
772}
773"#,
774            r#"
775struct X();
776mod y {
777    struct Y();
778    mod z {
779    }
780}
781"#,
782        );
783    }
784
785    #[test]
786    fn remove_unused_glob() {
787        check_assist(
788            remove_unused_imports,
789            r#"
790struct X();
791struct Y();
792mod z {
793    use super::*$0;
794}
795"#,
796            r#"
797struct X();
798struct Y();
799mod z {
800}
801"#,
802        );
803    }
804
805    #[test]
806    fn remove_unused_braced_glob() {
807        check_assist(
808            remove_unused_imports,
809            r#"
810struct X();
811struct Y();
812mod z {
813    use super::{*}$0;
814}
815"#,
816            r#"
817struct X();
818struct Y();
819mod z {
820}
821"#,
822        );
823    }
824
825    #[test]
826    fn remove_unused_fixes_nested_self() {
827        check_assist(
828            remove_unused_imports,
829            r#"
830mod inner {
831    pub struct X();
832    pub struct Y();
833}
834
835mod z {
836    use super::inner::{self, X}$0;
837
838    fn f() {
839        let y = inner::Y();
840    }
841}
842"#,
843            r#"mod inner {
844    pub struct X();
845    pub struct Y();
846}
847
848mod z {
849    use super::inner::{self};
850
851    fn f() {
852        let y = inner::Y();
853    }
854}
855"#,
856        );
857    }
858
859    #[test]
860    fn dont_remove_used_glob() {
861        check_assist_not_applicable(
862            remove_unused_imports,
863            r#"
864struct X();
865struct Y();
866mod z {
867    use super::*$0;
868
869    fn f() {
870        let x = X();
871    }
872}
873"#,
874        );
875    }
876
877    #[test]
878    fn only_remove_from_selection() {
879        check_assist(
880            remove_unused_imports,
881            r#"
882struct X();
883struct Y();
884mod z {
885    $0use super::X;$0
886    use super::Y;
887}
888mod w {
889    use super::Y;
890}
891"#,
892            r#"
893struct X();
894struct Y();
895mod z {
896    use super::Y;
897}
898mod w {
899    use super::Y;
900}
901"#,
902        );
903    }
904
905    #[test]
906    fn test_several_files() {
907        check_assist(
908            remove_unused_imports,
909            r#"
910//- /foo.rs
911pub struct X();
912pub struct Y();
913
914//- /main.rs
915$0use foo::X;
916use foo::Y;
917$0
918mod foo;
919mod z {
920    use crate::foo::X;
921}
922"#,
923            r#"
924
925mod foo;
926mod z {
927    use crate::foo::X;
928}
929"#,
930        );
931    }
932
933    #[test]
934    fn use_in_submodule_doesnt_count() {
935        check_assist(
936            remove_unused_imports,
937            r#"
938struct X();
939mod z {
940    use super::X$0;
941
942    mod w {
943        use crate::X;
944
945        fn f() {
946            let x = X();
947        }
948    }
949}
950"#,
951            r#"
952struct X();
953mod z {
954
955    mod w {
956        use crate::X;
957
958        fn f() {
959            let x = X();
960        }
961    }
962}
963"#,
964        );
965    }
966
967    #[test]
968    fn use_in_submodule_file_doesnt_count() {
969        check_assist(
970            remove_unused_imports,
971            r#"
972//- /z/foo.rs
973use crate::X;
974fn f() {
975    let x = X();
976}
977
978//- /main.rs
979pub struct X();
980
981mod z {
982    use crate::X$0;
983    mod foo;
984}
985"#,
986            r#"
987pub struct X();
988
989mod z {
990    mod foo;
991}
992"#,
993        );
994    }
995
996    #[test]
997    fn use_as_alias() {
998        check_assist_not_applicable(
999            remove_unused_imports,
1000            r#"
1001mod foo {
1002    pub struct Foo {}
1003}
1004
1005use foo::Foo as Bar$0;
1006
1007fn test(_: Bar) {}
1008"#,
1009        );
1010
1011        check_assist(
1012            remove_unused_imports,
1013            r#"
1014mod foo {
1015    pub struct Foo {}
1016    pub struct Bar {}
1017    pub struct Qux {}
1018    pub trait Quux {
1019        fn quxx(&self) {}
1020    }
1021    impl<T> Quxx for T {}
1022}
1023
1024use foo::{Foo as Bar, Bar as Baz, Qux as _, Quxx as _}$0;
1025
1026fn test(_: Bar) {
1027    let a = ();
1028    a.quxx();
1029}
1030"#,
1031            r#"
1032mod foo {
1033    pub struct Foo {}
1034    pub struct Bar {}
1035    pub struct Qux {}
1036    pub trait Quux {
1037        fn quxx(&self) {}
1038    }
1039    impl<T> Quxx for T {}
1040}
1041
1042use foo::{Foo as Bar, Quxx as _};
1043
1044fn test(_: Bar) {
1045    let a = ();
1046    a.quxx();
1047}
1048"#,
1049        );
1050    }
1051
1052    #[test]
1053    fn test_unused_macro() {
1054        check_assist(
1055            remove_unused_imports,
1056            r#"
1057//- /foo.rs crate:foo
1058#[macro_export]
1059macro_rules! m { () => {} }
1060
1061//- /main.rs crate:main deps:foo
1062use foo::m;$0
1063fn main() {}
1064"#,
1065            r#"
1066fn main() {}
1067"#,
1068        );
1069
1070        check_assist_not_applicable(
1071            remove_unused_imports,
1072            r#"
1073//- /foo.rs crate:foo
1074#[macro_export]
1075macro_rules! m { () => {} }
1076
1077//- /main.rs crate:main deps:foo
1078use foo::m;$0
1079fn main() {
1080    m!();
1081}
1082"#,
1083        );
1084
1085        check_assist_not_applicable(
1086            remove_unused_imports,
1087            r#"
1088//- /foo.rs crate:foo
1089#[macro_export]
1090macro_rules! m { () => {} }
1091
1092//- /bar.rs crate:bar deps:foo
1093pub use foo::m;
1094fn m() {}
1095
1096
1097//- /main.rs crate:main deps:bar
1098use bar::m;$0
1099fn main() {
1100    m!();
1101}
1102"#,
1103        );
1104    }
1105
1106    #[test]
1107    fn test_conflict_derive_macro() {
1108        check_assist_not_applicable(
1109            remove_unused_imports,
1110            r#"
1111//- proc_macros: derive_identity
1112//- minicore: derive
1113//- /bar.rs crate:bar
1114pub use proc_macros::DeriveIdentity;
1115pub trait DeriveIdentity {}
1116
1117//- /main.rs crate:main deps:bar
1118$0use bar::DeriveIdentity;$0
1119#[derive(DeriveIdentity)]
1120struct S;
1121"#,
1122        );
1123
1124        check_assist_not_applicable(
1125            remove_unused_imports,
1126            r#"
1127//- proc_macros: derive_identity
1128//- minicore: derive
1129//- /bar.rs crate:bar
1130pub use proc_macros::DeriveIdentity;
1131pub fn DeriveIdentity() {}
1132
1133//- /main.rs crate:main deps:bar
1134$0use bar::DeriveIdentity;$0
1135#[derive(DeriveIdentity)]
1136struct S;
1137"#,
1138        );
1139
1140        check_assist_not_applicable(
1141            remove_unused_imports,
1142            r#"
1143//- proc_macros: derive_identity
1144//- minicore: derive
1145//- /bar.rs crate:bar
1146pub use proc_macros::DeriveIdentity;
1147pub fn DeriveIdentity() {}
1148
1149//- /main.rs crate:main deps:bar
1150$0use bar::DeriveIdentity;$0
1151fn main() {
1152    DeriveIdentity();
1153}
1154"#,
1155        );
1156    }
1157}