aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2020-05-29 18:14:04 +0100
committerFlorian Diebold <[email protected]>2020-06-05 19:09:13 +0100
commita4a4a1854ebb53e1cdd7a5e3b308112bbbf3c676 (patch)
tree343d39c2a01bd3643bcab13eb01dfbd4f6a511cc /crates/ra_hir_ty
parent02f7b5d7abbab829c2a0f66cdcbb6678afb412a4 (diff)
Fix type parameter defaults
They should not be applied in expression or pattern contexts, unless there are other explicitly given type args.
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r--crates/ra_hir_ty/src/infer.rs4
-rw-r--r--crates/ra_hir_ty/src/infer/path.rs3
-rw-r--r--crates/ra_hir_ty/src/lower.rs70
-rw-r--r--crates/ra_hir_ty/src/tests/display_source_code.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/method_resolution.rs54
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs108
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs54
7 files changed, 187 insertions, 110 deletions
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index d9bf3c2f0..2e16e5120 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -439,13 +439,13 @@ impl<'a> InferenceContext<'a> {
439 }; 439 };
440 return match resolution { 440 return match resolution {
441 TypeNs::AdtId(AdtId::StructId(strukt)) => { 441 TypeNs::AdtId(AdtId::StructId(strukt)) => {
442 let substs = Ty::substs_from_path(&ctx, path, strukt.into()); 442 let substs = Ty::substs_from_path(&ctx, path, strukt.into(), true);
443 let ty = self.db.ty(strukt.into()); 443 let ty = self.db.ty(strukt.into());
444 let ty = self.insert_type_vars(ty.subst(&substs)); 444 let ty = self.insert_type_vars(ty.subst(&substs));
445 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) 445 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
446 } 446 }
447 TypeNs::EnumVariantId(var) => { 447 TypeNs::EnumVariantId(var) => {
448 let substs = Ty::substs_from_path(&ctx, path, var.into()); 448 let substs = Ty::substs_from_path(&ctx, path, var.into(), true);
449 let ty = self.db.ty(var.parent.into()); 449 let ty = self.db.ty(var.parent.into());
450 let ty = self.insert_type_vars(ty.subst(&substs)); 450 let ty = self.insert_type_vars(ty.subst(&substs));
451 forbid_unresolved_segments((ty, Some(var.into())), unresolved) 451 forbid_unresolved_segments((ty, Some(var.into())), unresolved)
diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs
index 1c2e56fb0..1ad0d8397 100644
--- a/crates/ra_hir_ty/src/infer/path.rs
+++ b/crates/ra_hir_ty/src/infer/path.rs
@@ -95,7 +95,7 @@ impl<'a> InferenceContext<'a> {
95 // self_subst is just for the parent 95 // self_subst is just for the parent
96 let parent_substs = self_subst.unwrap_or_else(Substs::empty); 96 let parent_substs = self_subst.unwrap_or_else(Substs::empty);
97 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); 97 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
98 let substs = Ty::substs_from_path(&ctx, path, typable); 98 let substs = Ty::substs_from_path(&ctx, path, typable, true);
99 let full_substs = Substs::builder(substs.len()) 99 let full_substs = Substs::builder(substs.len())
100 .use_parent_substs(&parent_substs) 100 .use_parent_substs(&parent_substs)
101 .fill(substs.0[parent_substs.len()..].iter().cloned()) 101 .fill(substs.0[parent_substs.len()..].iter().cloned())
@@ -141,6 +141,7 @@ impl<'a> InferenceContext<'a> {
141 def, 141 def,
142 resolved_segment, 142 resolved_segment,
143 remaining_segments_for_ty, 143 remaining_segments_for_ty,
144 true,
144 ); 145 );
145 if let Ty::Unknown = ty { 146 if let Ty::Unknown = ty {
146 return None; 147 return None;
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index a05cbd7fc..42713928f 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -323,6 +323,7 @@ impl Ty {
323 resolution: TypeNs, 323 resolution: TypeNs,
324 resolved_segment: PathSegment<'_>, 324 resolved_segment: PathSegment<'_>,
325 remaining_segments: PathSegments<'_>, 325 remaining_segments: PathSegments<'_>,
326 infer_args: bool,
326 ) -> (Ty, Option<TypeNs>) { 327 ) -> (Ty, Option<TypeNs>) {
327 let ty = match resolution { 328 let ty = match resolution {
328 TypeNs::TraitId(trait_) => { 329 TypeNs::TraitId(trait_) => {
@@ -400,9 +401,15 @@ impl Ty {
400 ctx.db.ty(adt.into()).subst(&substs) 401 ctx.db.ty(adt.into()).subst(&substs)
401 } 402 }
402 403
403 TypeNs::AdtId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 404 TypeNs::AdtId(it) => {
404 TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 405 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
405 TypeNs::TypeAliasId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 406 }
407 TypeNs::BuiltinType(it) => {
408 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
409 }
410 TypeNs::TypeAliasId(it) => {
411 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
412 }
406 // FIXME: report error 413 // FIXME: report error
407 TypeNs::EnumVariantId(_) => return (Ty::Unknown, None), 414 TypeNs::EnumVariantId(_) => return (Ty::Unknown, None),
408 }; 415 };
@@ -428,7 +435,13 @@ impl Ty {
428 ), 435 ),
429 Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)), 436 Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
430 }; 437 };
431 Ty::from_partly_resolved_hir_path(ctx, resolution, resolved_segment, remaining_segments) 438 Ty::from_partly_resolved_hir_path(
439 ctx,
440 resolution,
441 resolved_segment,
442 remaining_segments,
443 false,
444 )
432 } 445 }
433 446
434 fn select_associated_type( 447 fn select_associated_type(
@@ -474,13 +487,14 @@ impl Ty {
474 ctx: &TyLoweringContext<'_>, 487 ctx: &TyLoweringContext<'_>,
475 segment: PathSegment<'_>, 488 segment: PathSegment<'_>,
476 typable: TyDefId, 489 typable: TyDefId,
490 infer_args: bool,
477 ) -> Ty { 491 ) -> Ty {
478 let generic_def = match typable { 492 let generic_def = match typable {
479 TyDefId::BuiltinType(_) => None, 493 TyDefId::BuiltinType(_) => None,
480 TyDefId::AdtId(it) => Some(it.into()), 494 TyDefId::AdtId(it) => Some(it.into()),
481 TyDefId::TypeAliasId(it) => Some(it.into()), 495 TyDefId::TypeAliasId(it) => Some(it.into()),
482 }; 496 };
483 let substs = substs_from_path_segment(ctx, segment, generic_def, false); 497 let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args);
484 ctx.db.ty(typable).subst(&substs) 498 ctx.db.ty(typable).subst(&substs)
485 } 499 }
486 500
@@ -493,6 +507,7 @@ impl Ty {
493 // `ValueTyDefId` is just a convenient way to pass generics and 507 // `ValueTyDefId` is just a convenient way to pass generics and
494 // special-case enum variants 508 // special-case enum variants
495 resolved: ValueTyDefId, 509 resolved: ValueTyDefId,
510 infer_args: bool,
496 ) -> Substs { 511 ) -> Substs {
497 let last = path.segments().last().expect("path should have at least one segment"); 512 let last = path.segments().last().expect("path should have at least one segment");
498 let (segment, generic_def) = match resolved { 513 let (segment, generic_def) = match resolved {
@@ -515,22 +530,27 @@ impl Ty {
515 (segment, Some(var.parent.into())) 530 (segment, Some(var.parent.into()))
516 } 531 }
517 }; 532 };
518 substs_from_path_segment(ctx, segment, generic_def, false) 533 substs_from_path_segment(ctx, segment, generic_def, infer_args)
519 } 534 }
520} 535}
521 536
522pub(super) fn substs_from_path_segment( 537fn substs_from_path_segment(
523 ctx: &TyLoweringContext<'_>, 538 ctx: &TyLoweringContext<'_>,
524 segment: PathSegment<'_>, 539 segment: PathSegment<'_>,
525 def_generic: Option<GenericDefId>, 540 def_generic: Option<GenericDefId>,
526 _add_self_param: bool, 541 infer_args: bool,
527) -> Substs { 542) -> Substs {
528 let mut substs = Vec::new(); 543 let mut substs = Vec::new();
529 let def_generics = def_generic.map(|def| generics(ctx.db.upcast(), def)); 544 let def_generics = def_generic.map(|def| generics(ctx.db.upcast(), def));
530 545
531 let (parent_params, self_params, type_params, impl_trait_params) = 546 let (parent_params, self_params, type_params, impl_trait_params) =
532 def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split()); 547 def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split());
548 let total_len = parent_params + self_params + type_params + impl_trait_params;
549
533 substs.extend(iter::repeat(Ty::Unknown).take(parent_params)); 550 substs.extend(iter::repeat(Ty::Unknown).take(parent_params));
551
552 let mut had_explicit_args = false;
553
534 if let Some(generic_args) = &segment.args_and_bindings { 554 if let Some(generic_args) = &segment.args_and_bindings {
535 if !generic_args.has_self_type { 555 if !generic_args.has_self_type {
536 substs.extend(iter::repeat(Ty::Unknown).take(self_params)); 556 substs.extend(iter::repeat(Ty::Unknown).take(self_params));
@@ -542,31 +562,35 @@ pub(super) fn substs_from_path_segment(
542 for arg in generic_args.args.iter().skip(skip).take(expected_num) { 562 for arg in generic_args.args.iter().skip(skip).take(expected_num) {
543 match arg { 563 match arg {
544 GenericArg::Type(type_ref) => { 564 GenericArg::Type(type_ref) => {
565 had_explicit_args = true;
545 let ty = Ty::from_hir(ctx, type_ref); 566 let ty = Ty::from_hir(ctx, type_ref);
546 substs.push(ty); 567 substs.push(ty);
547 } 568 }
548 } 569 }
549 } 570 }
550 } 571 }
551 let total_len = parent_params + self_params + type_params + impl_trait_params;
552 // add placeholders for args that were not provided
553 for _ in substs.len()..total_len {
554 substs.push(Ty::Unknown);
555 }
556 assert_eq!(substs.len(), total_len);
557 572
558 // handle defaults 573 // handle defaults. In expression or pattern path segments without
559 if let Some(def_generic) = def_generic { 574 // explicitly specified type arguments, missing type arguments are inferred
560 let default_substs = ctx.db.generic_defaults(def_generic); 575 // (i.e. defaults aren't used).
561 assert_eq!(substs.len(), default_substs.len()); 576 if !infer_args || had_explicit_args {
577 if let Some(def_generic) = def_generic {
578 let default_substs = ctx.db.generic_defaults(def_generic);
579 assert_eq!(total_len, default_substs.len());
562 580
563 for (i, default_ty) in default_substs.iter().enumerate() { 581 for default_ty in default_substs.iter().skip(substs.len()) {
564 if substs[i] == Ty::Unknown { 582 substs.push(default_ty.clone());
565 substs[i] = default_ty.clone();
566 } 583 }
567 } 584 }
568 } 585 }
569 586
587 // add placeholders for args that were not provided
588 // FIXME: emit diagnostics in contexts where this is not allowed
589 for _ in substs.len()..total_len {
590 substs.push(Ty::Unknown);
591 }
592 assert_eq!(substs.len(), total_len);
593
570 Substs(substs.into()) 594 Substs(substs.into())
571} 595}
572 596
@@ -615,9 +639,7 @@ impl TraitRef {
615 segment: PathSegment<'_>, 639 segment: PathSegment<'_>,
616 resolved: TraitId, 640 resolved: TraitId,
617 ) -> Substs { 641 ) -> Substs {
618 let has_self_param = 642 substs_from_path_segment(ctx, segment, Some(resolved.into()), false)
619 segment.args_and_bindings.as_ref().map(|a| a.has_self_type).unwrap_or(false);
620 substs_from_path_segment(ctx, segment, Some(resolved.into()), !has_self_param)
621 } 643 }
622 644
623 pub(crate) fn from_type_bound( 645 pub(crate) fn from_type_bound(
diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs
index 4088b1d22..5dfa0a014 100644
--- a/crates/ra_hir_ty/src/tests/display_source_code.rs
+++ b/crates/ra_hir_ty/src/tests/display_source_code.rs
@@ -29,7 +29,7 @@ fn omit_default_type_parameters() {
29 //- /main.rs 29 //- /main.rs
30 struct Foo<T = u8> { t: T } 30 struct Foo<T = u8> { t: T }
31 fn main() { 31 fn main() {
32 let foo = Foo { t: 5 }; 32 let foo = Foo { t: 5u8 };
33 foo<|>; 33 foo<|>;
34 } 34 }
35 ", 35 ",
@@ -41,7 +41,7 @@ fn omit_default_type_parameters() {
41 //- /main.rs 41 //- /main.rs
42 struct Foo<K, T = u8> { k: K, t: T } 42 struct Foo<K, T = u8> { k: K, t: T }
43 fn main() { 43 fn main() {
44 let foo = Foo { k: 400, t: 5 }; 44 let foo = Foo { k: 400, t: 5u8 };
45 foo<|>; 45 foo<|>;
46 } 46 }
47 ", 47 ",
diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs
index 558a70022..804297315 100644
--- a/crates/ra_hir_ty/src/tests/method_resolution.rs
+++ b/crates/ra_hir_ty/src/tests/method_resolution.rs
@@ -184,60 +184,6 @@ fn test() {
184} 184}
185 185
186#[test] 186#[test]
187fn infer_associated_method_generics_with_default_param() {
188 assert_snapshot!(
189 infer(r#"
190struct Gen<T=u32> {
191 val: T
192}
193
194impl<T> Gen<T> {
195 pub fn make() -> Gen<T> {
196 loop { }
197 }
198}
199
200fn test() {
201 let a = Gen::make();
202}
203"#),
204 @r###"
205 80..104 '{ ... }': Gen<T>
206 90..98 'loop { }': !
207 95..98 '{ }': ()
208 118..146 '{ ...e(); }': ()
209 128..129 'a': Gen<u32>
210 132..141 'Gen::make': fn make<u32>() -> Gen<u32>
211 132..143 'Gen::make()': Gen<u32>
212 "###
213 );
214}
215
216#[test]
217fn infer_associated_method_generics_with_default_tuple_param() {
218 let t = type_at(
219 r#"
220//- /main.rs
221struct Gen<T=()> {
222 val: T
223}
224
225impl<T> Gen<T> {
226 pub fn make() -> Gen<T> {
227 loop { }
228 }
229}
230
231fn test() {
232 let a = Gen::make();
233 a.val<|>;
234}
235"#,
236 );
237 assert_eq!(t, "()");
238}
239
240#[test]
241fn infer_associated_method_generics_without_args() { 187fn infer_associated_method_generics_without_args() {
242 assert_snapshot!( 188 assert_snapshot!(
243 infer(r#" 189 infer(r#"
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 88309157b..8a5031756 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -1997,3 +1997,111 @@ fn foo() {
1997 "### 1997 "###
1998 ); 1998 );
1999} 1999}
2000
2001#[test]
2002fn generic_default() {
2003 assert_snapshot!(
2004 infer(r#"
2005struct Thing<T = ()> { t: T }
2006enum OtherThing<T = ()> {
2007 One { t: T },
2008 Two(T),
2009}
2010
2011fn test(t1: Thing, t2: OtherThing, t3: Thing<i32>, t4: OtherThing<i32>) {
2012 t1.t;
2013 t3.t;
2014 match t2 {
2015 OtherThing::One { t } => { t; },
2016 OtherThing::Two(t) => { t; },
2017 }
2018 match t4 {
2019 OtherThing::One { t } => { t; },
2020 OtherThing::Two(t) => { t; },
2021 }
2022}
2023"#),
2024 @r###"
2025 98..100 't1': Thing<()>
2026 109..111 't2': OtherThing<()>
2027 125..127 't3': Thing<i32>
2028 141..143 't4': OtherThing<i32>
2029 162..385 '{ ... } }': ()
2030 168..170 't1': Thing<()>
2031 168..172 't1.t': ()
2032 178..180 't3': Thing<i32>
2033 178..182 't3.t': i32
2034 188..283 'match ... }': ()
2035 194..196 't2': OtherThing<()>
2036 207..228 'OtherT... { t }': OtherThing<()>
2037 225..226 't': ()
2038 232..238 '{ t; }': ()
2039 234..235 't': ()
2040 248..266 'OtherT...Two(t)': OtherThing<()>
2041 264..265 't': ()
2042 270..276 '{ t; }': ()
2043 272..273 't': ()
2044 288..383 'match ... }': ()
2045 294..296 't4': OtherThing<i32>
2046 307..328 'OtherT... { t }': OtherThing<i32>
2047 325..326 't': i32
2048 332..338 '{ t; }': ()
2049 334..335 't': i32
2050 348..366 'OtherT...Two(t)': OtherThing<i32>
2051 364..365 't': i32
2052 370..376 '{ t; }': ()
2053 372..373 't': i32
2054 "###
2055 );
2056}
2057
2058#[test]
2059fn generic_default_in_struct_literal() {
2060 assert_snapshot!(
2061 infer(r#"
2062struct Thing<T = ()> { t: T }
2063enum OtherThing<T = ()> {
2064 One { t: T },
2065 Two(T),
2066}
2067
2068fn test() {
2069 let x = Thing { t: loop {} };
2070 let y = Thing { t: () };
2071 let z = Thing { t: 1i32 };
2072 if let Thing { t } = z {
2073 t;
2074 }
2075
2076 let a = OtherThing::One { t: 1i32 };
2077 let b = OtherThing::Two(1i32);
2078}
2079"#),
2080 @r###"
2081 100..320 '{ ...32); }': ()
2082 110..111 'x': Thing<!>
2083 114..134 'Thing ...p {} }': Thing<!>
2084 125..132 'loop {}': !
2085 130..132 '{}': ()
2086 144..145 'y': Thing<()>
2087 148..163 'Thing { t: () }': Thing<()>
2088 159..161 '()': ()
2089 173..174 'z': Thing<i32>
2090 177..194 'Thing ...1i32 }': Thing<i32>
2091 188..192 '1i32': i32
2092 200..241 'if let... }': ()
2093 207..218 'Thing { t }': Thing<i32>
2094 215..216 't': i32
2095 221..222 'z': Thing<i32>
2096 223..241 '{ ... }': ()
2097 233..234 't': i32
2098 251..252 'a': OtherThing<i32>
2099 255..282 'OtherT...1i32 }': OtherThing<i32>
2100 276..280 '1i32': i32
2101 292..293 'b': OtherThing<i32>
2102 296..311 'OtherThing::Two': Two<i32>(i32) -> OtherThing<i32>
2103 296..317 'OtherT...(1i32)': OtherThing<i32>
2104 312..316 '1i32': i32
2105 "###
2106 );
2107}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 0c538a62d..133fb5f39 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -1806,33 +1806,33 @@ fn test() {
1806} 1806}
1807"#), 1807"#),
1808 @r###" 1808 @r###"
180965..69 'self': &Self 1809 65..69 'self': &Self
1810166..170 'self': Self 1810 166..170 'self': Self
1811172..176 'args': Args 1811 172..176 'args': Args
1812240..244 'self': &Foo 1812 240..244 'self': &Foo
1813255..257 '{}': () 1813 255..257 '{}': ()
1814335..336 'f': F 1814 335..336 'f': F
1815355..357 '{}': () 1815 355..357 '{}': ()
1816444..690 '{ ...o(); }': () 1816 444..690 '{ ...o(); }': ()
1817454..459 'lazy1': Lazy<Foo, fn() -> T> 1817 454..459 'lazy1': Lazy<Foo, || -> Foo>
1818476..485 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> 1818 476..485 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
1819476..493 'Lazy::...| Foo)': Lazy<Foo, fn() -> T> 1819 476..493 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
1820486..492 '|| Foo': || -> T 1820 486..492 '|| Foo': || -> Foo
1821489..492 'Foo': Foo 1821 489..492 'Foo': Foo
1822503..505 'r1': {unknown} 1822 503..505 'r1': usize
1823508..513 'lazy1': Lazy<Foo, fn() -> T> 1823 508..513 'lazy1': Lazy<Foo, || -> Foo>
1824508..519 'lazy1.foo()': {unknown} 1824 508..519 'lazy1.foo()': usize
1825561..576 'make_foo_fn_ptr': fn() -> Foo 1825 561..576 'make_foo_fn_ptr': fn() -> Foo
1826592..603 'make_foo_fn': fn make_foo_fn() -> Foo 1826 592..603 'make_foo_fn': fn make_foo_fn() -> Foo
1827613..618 'lazy2': Lazy<Foo, fn() -> T> 1827 613..618 'lazy2': Lazy<Foo, fn() -> Foo>
1828635..644 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> 1828 635..644 'Lazy::new': fn new<Foo, fn() -> Foo>(fn() -> Foo) -> Lazy<Foo, fn() -> Foo>
1829635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> T> 1829 635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> Foo>
1830645..660 'make_foo_fn_ptr': fn() -> Foo 1830 645..660 'make_foo_fn_ptr': fn() -> Foo
1831671..673 'r2': {unknown} 1831 671..673 'r2': {unknown}
1832676..681 'lazy2': Lazy<Foo, fn() -> T> 1832 676..681 'lazy2': Lazy<Foo, fn() -> Foo>
1833676..687 'lazy2.foo()': {unknown} 1833 676..687 'lazy2.foo()': {unknown}
1834550..552 '{}': () 1834 550..552 '{}': ()
1835"### 1835 "###
1836 ); 1836 );
1837} 1837}
1838 1838