diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/cfg/src/cfg_expr.rs | 50 | ||||
-rw-r--r-- | crates/cfg/src/dnf.rs | 158 | ||||
-rw-r--r-- | crates/cfg/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/cfg/src/tests.rs | 193 |
4 files changed, 195 insertions, 208 deletions
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs index 283ff968f..42327f1e1 100644 --- a/crates/cfg/src/cfg_expr.rs +++ b/crates/cfg/src/cfg_expr.rs | |||
@@ -128,53 +128,3 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | |||
128 | } | 128 | } |
129 | Some(ret) | 129 | Some(ret) |
130 | } | 130 | } |
131 | |||
132 | #[cfg(test)] | ||
133 | mod tests { | ||
134 | use super::*; | ||
135 | |||
136 | use mbe::ast_to_token_tree; | ||
137 | use syntax::ast::{self, AstNode}; | ||
138 | |||
139 | fn assert_parse_result(input: &str, expected: CfgExpr) { | ||
140 | let (tt, _) = { | ||
141 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
142 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
143 | ast_to_token_tree(&tt).unwrap() | ||
144 | }; | ||
145 | let cfg = CfgExpr::parse(&tt); | ||
146 | assert_eq!(cfg, expected); | ||
147 | } | ||
148 | |||
149 | #[test] | ||
150 | fn test_cfg_expr_parser() { | ||
151 | assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into()); | ||
152 | assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into()); | ||
153 | assert_parse_result( | ||
154 | "#![cfg(not(foo))]", | ||
155 | CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())), | ||
156 | ); | ||
157 | assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); | ||
158 | |||
159 | // Only take the first | ||
160 | assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into()); | ||
161 | |||
162 | assert_parse_result( | ||
163 | r#"#![cfg(all(foo, bar = "baz"))]"#, | ||
164 | CfgExpr::All(vec![ | ||
165 | CfgAtom::Flag("foo".into()).into(), | ||
166 | CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), | ||
167 | ]), | ||
168 | ); | ||
169 | |||
170 | assert_parse_result( | ||
171 | r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, | ||
172 | CfgExpr::Any(vec![ | ||
173 | CfgExpr::Not(Box::new(CfgExpr::Invalid)), | ||
174 | CfgExpr::All(vec![]), | ||
175 | CfgExpr::Invalid, | ||
176 | CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), | ||
177 | ]), | ||
178 | ); | ||
179 | } | ||
180 | } | ||
diff --git a/crates/cfg/src/dnf.rs b/crates/cfg/src/dnf.rs index 7ee7b0062..580c9a9a2 100644 --- a/crates/cfg/src/dnf.rs +++ b/crates/cfg/src/dnf.rs | |||
@@ -318,161 +318,3 @@ fn make_nnf(expr: CfgExpr) -> CfgExpr { | |||
318 | }, | 318 | }, |
319 | } | 319 | } |
320 | } | 320 | } |
321 | |||
322 | #[cfg(test)] | ||
323 | mod test { | ||
324 | use expect_test::{expect, Expect}; | ||
325 | use mbe::ast_to_token_tree; | ||
326 | use syntax::{ast, AstNode}; | ||
327 | |||
328 | use super::*; | ||
329 | |||
330 | fn check_dnf(input: &str, expect: Expect) { | ||
331 | let (tt, _) = { | ||
332 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
333 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
334 | ast_to_token_tree(&tt).unwrap() | ||
335 | }; | ||
336 | let cfg = CfgExpr::parse(&tt); | ||
337 | let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); | ||
338 | expect.assert_eq(&actual); | ||
339 | } | ||
340 | |||
341 | fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { | ||
342 | let (tt, _) = { | ||
343 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
344 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
345 | ast_to_token_tree(&tt).unwrap() | ||
346 | }; | ||
347 | let cfg = CfgExpr::parse(&tt); | ||
348 | let dnf = DnfExpr::new(cfg); | ||
349 | let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); | ||
350 | expect.assert_eq(&why_inactive); | ||
351 | } | ||
352 | |||
353 | #[track_caller] | ||
354 | fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { | ||
355 | let (tt, _) = { | ||
356 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
357 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
358 | ast_to_token_tree(&tt).unwrap() | ||
359 | }; | ||
360 | let cfg = CfgExpr::parse(&tt); | ||
361 | let dnf = DnfExpr::new(cfg); | ||
362 | let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>(); | ||
363 | assert_eq!(hints, expected_hints); | ||
364 | } | ||
365 | |||
366 | #[test] | ||
367 | fn smoke() { | ||
368 | check_dnf("#![cfg(test)]", expect![[r#"#![cfg(test)]"#]]); | ||
369 | check_dnf("#![cfg(not(test))]", expect![[r#"#![cfg(not(test))]"#]]); | ||
370 | check_dnf("#![cfg(not(not(test)))]", expect![[r#"#![cfg(test)]"#]]); | ||
371 | |||
372 | check_dnf("#![cfg(all(a, b))]", expect![[r#"#![cfg(all(a, b))]"#]]); | ||
373 | check_dnf("#![cfg(any(a, b))]", expect![[r#"#![cfg(any(a, b))]"#]]); | ||
374 | |||
375 | check_dnf("#![cfg(not(a))]", expect![[r#"#![cfg(not(a))]"#]]); | ||
376 | } | ||
377 | |||
378 | #[test] | ||
379 | fn distribute() { | ||
380 | check_dnf("#![cfg(all(any(a, b), c))]", expect![[r#"#![cfg(any(all(a, c), all(b, c)))]"#]]); | ||
381 | check_dnf("#![cfg(all(c, any(a, b)))]", expect![[r#"#![cfg(any(all(c, a), all(c, b)))]"#]]); | ||
382 | check_dnf( | ||
383 | "#![cfg(all(any(a, b), any(c, d)))]", | ||
384 | expect![[r#"#![cfg(any(all(a, c), all(a, d), all(b, c), all(b, d)))]"#]], | ||
385 | ); | ||
386 | |||
387 | check_dnf( | ||
388 | "#![cfg(all(any(a, b, c), any(d, e, f), g))]", | ||
389 | expect![[ | ||
390 | r#"#![cfg(any(all(a, d, g), all(a, e, g), all(a, f, g), all(b, d, g), all(b, e, g), all(b, f, g), all(c, d, g), all(c, e, g), all(c, f, g)))]"# | ||
391 | ]], | ||
392 | ); | ||
393 | } | ||
394 | |||
395 | #[test] | ||
396 | fn demorgan() { | ||
397 | check_dnf("#![cfg(not(all(a, b)))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]); | ||
398 | check_dnf("#![cfg(not(any(a, b)))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]); | ||
399 | |||
400 | check_dnf("#![cfg(not(all(not(a), b)))]", expect![[r#"#![cfg(any(a, not(b)))]"#]]); | ||
401 | check_dnf("#![cfg(not(any(a, not(b))))]", expect![[r#"#![cfg(all(not(a), b))]"#]]); | ||
402 | } | ||
403 | |||
404 | #[test] | ||
405 | fn nested() { | ||
406 | check_dnf( | ||
407 | "#![cfg(all(any(a), not(all(any(b)))))]", | ||
408 | expect![[r#"#![cfg(all(a, not(b)))]"#]], | ||
409 | ); | ||
410 | |||
411 | check_dnf("#![cfg(any(any(a, b)))]", expect![[r#"#![cfg(any(a, b))]"#]]); | ||
412 | check_dnf("#![cfg(not(any(any(a, b))))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]); | ||
413 | check_dnf("#![cfg(all(all(a, b)))]", expect![[r#"#![cfg(all(a, b))]"#]]); | ||
414 | check_dnf("#![cfg(not(all(all(a, b))))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]); | ||
415 | } | ||
416 | |||
417 | #[test] | ||
418 | fn hints() { | ||
419 | let mut opts = CfgOptions::default(); | ||
420 | |||
421 | check_enable_hints("#![cfg(test)]", &opts, &["enable test"]); | ||
422 | check_enable_hints("#![cfg(not(test))]", &opts, &[]); | ||
423 | |||
424 | check_enable_hints("#![cfg(any(a, b))]", &opts, &["enable a", "enable b"]); | ||
425 | check_enable_hints("#![cfg(any(b, a))]", &opts, &["enable b", "enable a"]); | ||
426 | |||
427 | check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]); | ||
428 | |||
429 | opts.insert_atom("test".into()); | ||
430 | |||
431 | check_enable_hints("#![cfg(test)]", &opts, &[]); | ||
432 | check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]); | ||
433 | } | ||
434 | |||
435 | /// Tests that we don't suggest hints for cfgs that express an inconsistent formula. | ||
436 | #[test] | ||
437 | fn hints_impossible() { | ||
438 | let mut opts = CfgOptions::default(); | ||
439 | |||
440 | check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); | ||
441 | |||
442 | opts.insert_atom("test".into()); | ||
443 | |||
444 | check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); | ||
445 | } | ||
446 | |||
447 | #[test] | ||
448 | fn why_inactive() { | ||
449 | let mut opts = CfgOptions::default(); | ||
450 | opts.insert_atom("test".into()); | ||
451 | opts.insert_atom("test2".into()); | ||
452 | |||
453 | check_why_inactive("#![cfg(a)]", &opts, expect![["a is disabled"]]); | ||
454 | check_why_inactive("#![cfg(not(test))]", &opts, expect![["test is enabled"]]); | ||
455 | |||
456 | check_why_inactive( | ||
457 | "#![cfg(all(not(test), not(test2)))]", | ||
458 | &opts, | ||
459 | expect![["test and test2 are enabled"]], | ||
460 | ); | ||
461 | check_why_inactive("#![cfg(all(a, b))]", &opts, expect![["a and b are disabled"]]); | ||
462 | check_why_inactive( | ||
463 | "#![cfg(all(not(test), a))]", | ||
464 | &opts, | ||
465 | expect![["test is enabled and a is disabled"]], | ||
466 | ); | ||
467 | check_why_inactive( | ||
468 | "#![cfg(all(not(test), test2, a))]", | ||
469 | &opts, | ||
470 | expect![["test is enabled and a is disabled"]], | ||
471 | ); | ||
472 | check_why_inactive( | ||
473 | "#![cfg(all(not(test), not(test2), a))]", | ||
474 | &opts, | ||
475 | expect![["test and test2 are enabled and a is disabled"]], | ||
476 | ); | ||
477 | } | ||
478 | } | ||
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 28a40a082..d0e08cf5f 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs | |||
@@ -2,6 +2,8 @@ | |||
2 | 2 | ||
3 | mod cfg_expr; | 3 | mod cfg_expr; |
4 | mod dnf; | 4 | mod dnf; |
5 | #[cfg(test)] | ||
6 | mod tests; | ||
5 | 7 | ||
6 | use std::fmt; | 8 | use std::fmt; |
7 | 9 | ||
diff --git a/crates/cfg/src/tests.rs b/crates/cfg/src/tests.rs new file mode 100644 index 000000000..bd0f9ec48 --- /dev/null +++ b/crates/cfg/src/tests.rs | |||
@@ -0,0 +1,193 @@ | |||
1 | use expect_test::{expect, Expect}; | ||
2 | use mbe::ast_to_token_tree; | ||
3 | use syntax::{ast, AstNode}; | ||
4 | |||
5 | use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; | ||
6 | |||
7 | fn assert_parse_result(input: &str, expected: CfgExpr) { | ||
8 | let (tt, _) = { | ||
9 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
10 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
11 | ast_to_token_tree(&tt).unwrap() | ||
12 | }; | ||
13 | let cfg = CfgExpr::parse(&tt); | ||
14 | assert_eq!(cfg, expected); | ||
15 | } | ||
16 | |||
17 | fn check_dnf(input: &str, expect: Expect) { | ||
18 | let (tt, _) = { | ||
19 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
20 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
21 | ast_to_token_tree(&tt).unwrap() | ||
22 | }; | ||
23 | let cfg = CfgExpr::parse(&tt); | ||
24 | let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); | ||
25 | expect.assert_eq(&actual); | ||
26 | } | ||
27 | |||
28 | fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { | ||
29 | let (tt, _) = { | ||
30 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
31 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
32 | ast_to_token_tree(&tt).unwrap() | ||
33 | }; | ||
34 | let cfg = CfgExpr::parse(&tt); | ||
35 | let dnf = DnfExpr::new(cfg); | ||
36 | let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); | ||
37 | expect.assert_eq(&why_inactive); | ||
38 | } | ||
39 | |||
40 | #[track_caller] | ||
41 | fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { | ||
42 | let (tt, _) = { | ||
43 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
44 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
45 | ast_to_token_tree(&tt).unwrap() | ||
46 | }; | ||
47 | let cfg = CfgExpr::parse(&tt); | ||
48 | let dnf = DnfExpr::new(cfg); | ||
49 | let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>(); | ||
50 | assert_eq!(hints, expected_hints); | ||
51 | } | ||
52 | |||
53 | #[test] | ||
54 | fn test_cfg_expr_parser() { | ||
55 | assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into()); | ||
56 | assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into()); | ||
57 | assert_parse_result( | ||
58 | "#![cfg(not(foo))]", | ||
59 | CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())), | ||
60 | ); | ||
61 | assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); | ||
62 | |||
63 | // Only take the first | ||
64 | assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into()); | ||
65 | |||
66 | assert_parse_result( | ||
67 | r#"#![cfg(all(foo, bar = "baz"))]"#, | ||
68 | CfgExpr::All(vec![ | ||
69 | CfgAtom::Flag("foo".into()).into(), | ||
70 | CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), | ||
71 | ]), | ||
72 | ); | ||
73 | |||
74 | assert_parse_result( | ||
75 | r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, | ||
76 | CfgExpr::Any(vec![ | ||
77 | CfgExpr::Not(Box::new(CfgExpr::Invalid)), | ||
78 | CfgExpr::All(vec![]), | ||
79 | CfgExpr::Invalid, | ||
80 | CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), | ||
81 | ]), | ||
82 | ); | ||
83 | } | ||
84 | |||
85 | #[test] | ||
86 | fn smoke() { | ||
87 | check_dnf("#![cfg(test)]", expect![[r#"#![cfg(test)]"#]]); | ||
88 | check_dnf("#![cfg(not(test))]", expect![[r#"#![cfg(not(test))]"#]]); | ||
89 | check_dnf("#![cfg(not(not(test)))]", expect![[r#"#![cfg(test)]"#]]); | ||
90 | |||
91 | check_dnf("#![cfg(all(a, b))]", expect![[r#"#![cfg(all(a, b))]"#]]); | ||
92 | check_dnf("#![cfg(any(a, b))]", expect![[r#"#![cfg(any(a, b))]"#]]); | ||
93 | |||
94 | check_dnf("#![cfg(not(a))]", expect![[r#"#![cfg(not(a))]"#]]); | ||
95 | } | ||
96 | |||
97 | #[test] | ||
98 | fn distribute() { | ||
99 | check_dnf("#![cfg(all(any(a, b), c))]", expect![[r#"#![cfg(any(all(a, c), all(b, c)))]"#]]); | ||
100 | check_dnf("#![cfg(all(c, any(a, b)))]", expect![[r#"#![cfg(any(all(c, a), all(c, b)))]"#]]); | ||
101 | check_dnf( | ||
102 | "#![cfg(all(any(a, b), any(c, d)))]", | ||
103 | expect![[r#"#![cfg(any(all(a, c), all(a, d), all(b, c), all(b, d)))]"#]], | ||
104 | ); | ||
105 | |||
106 | check_dnf( | ||
107 | "#![cfg(all(any(a, b, c), any(d, e, f), g))]", | ||
108 | expect![[ | ||
109 | r#"#![cfg(any(all(a, d, g), all(a, e, g), all(a, f, g), all(b, d, g), all(b, e, g), all(b, f, g), all(c, d, g), all(c, e, g), all(c, f, g)))]"# | ||
110 | ]], | ||
111 | ); | ||
112 | } | ||
113 | |||
114 | #[test] | ||
115 | fn demorgan() { | ||
116 | check_dnf("#![cfg(not(all(a, b)))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]); | ||
117 | check_dnf("#![cfg(not(any(a, b)))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]); | ||
118 | |||
119 | check_dnf("#![cfg(not(all(not(a), b)))]", expect![[r#"#![cfg(any(a, not(b)))]"#]]); | ||
120 | check_dnf("#![cfg(not(any(a, not(b))))]", expect![[r#"#![cfg(all(not(a), b))]"#]]); | ||
121 | } | ||
122 | |||
123 | #[test] | ||
124 | fn nested() { | ||
125 | check_dnf("#![cfg(all(any(a), not(all(any(b)))))]", expect![[r#"#![cfg(all(a, not(b)))]"#]]); | ||
126 | |||
127 | check_dnf("#![cfg(any(any(a, b)))]", expect![[r#"#![cfg(any(a, b))]"#]]); | ||
128 | check_dnf("#![cfg(not(any(any(a, b))))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]); | ||
129 | check_dnf("#![cfg(all(all(a, b)))]", expect![[r#"#![cfg(all(a, b))]"#]]); | ||
130 | check_dnf("#![cfg(not(all(all(a, b))))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]); | ||
131 | } | ||
132 | |||
133 | #[test] | ||
134 | fn hints() { | ||
135 | let mut opts = CfgOptions::default(); | ||
136 | |||
137 | check_enable_hints("#![cfg(test)]", &opts, &["enable test"]); | ||
138 | check_enable_hints("#![cfg(not(test))]", &opts, &[]); | ||
139 | |||
140 | check_enable_hints("#![cfg(any(a, b))]", &opts, &["enable a", "enable b"]); | ||
141 | check_enable_hints("#![cfg(any(b, a))]", &opts, &["enable b", "enable a"]); | ||
142 | |||
143 | check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]); | ||
144 | |||
145 | opts.insert_atom("test".into()); | ||
146 | |||
147 | check_enable_hints("#![cfg(test)]", &opts, &[]); | ||
148 | check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]); | ||
149 | } | ||
150 | |||
151 | /// Tests that we don't suggest hints for cfgs that express an inconsistent formula. | ||
152 | #[test] | ||
153 | fn hints_impossible() { | ||
154 | let mut opts = CfgOptions::default(); | ||
155 | |||
156 | check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); | ||
157 | |||
158 | opts.insert_atom("test".into()); | ||
159 | |||
160 | check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); | ||
161 | } | ||
162 | |||
163 | #[test] | ||
164 | fn why_inactive() { | ||
165 | let mut opts = CfgOptions::default(); | ||
166 | opts.insert_atom("test".into()); | ||
167 | opts.insert_atom("test2".into()); | ||
168 | |||
169 | check_why_inactive("#![cfg(a)]", &opts, expect![["a is disabled"]]); | ||
170 | check_why_inactive("#![cfg(not(test))]", &opts, expect![["test is enabled"]]); | ||
171 | |||
172 | check_why_inactive( | ||
173 | "#![cfg(all(not(test), not(test2)))]", | ||
174 | &opts, | ||
175 | expect![["test and test2 are enabled"]], | ||
176 | ); | ||
177 | check_why_inactive("#![cfg(all(a, b))]", &opts, expect![["a and b are disabled"]]); | ||
178 | check_why_inactive( | ||
179 | "#![cfg(all(not(test), a))]", | ||
180 | &opts, | ||
181 | expect![["test is enabled and a is disabled"]], | ||
182 | ); | ||
183 | check_why_inactive( | ||
184 | "#![cfg(all(not(test), test2, a))]", | ||
185 | &opts, | ||
186 | expect![["test is enabled and a is disabled"]], | ||
187 | ); | ||
188 | check_why_inactive( | ||
189 | "#![cfg(all(not(test), not(test2), a))]", | ||
190 | &opts, | ||
191 | expect![["test and test2 are enabled and a is disabled"]], | ||
192 | ); | ||
193 | } | ||