Chromium Code Reviews| Index: pkg/analyzer/test/src/summary/summary_common.dart |
| diff --git a/pkg/analyzer/test/src/summary/summary_common.dart b/pkg/analyzer/test/src/summary/summary_common.dart |
| index 23571cc07865ad7a14ea4c7f3e335be1ca7a6a6b..ec83adef970182d5892ff15540b12816c1d91a97 100644 |
| --- a/pkg/analyzer/test/src/summary/summary_common.dart |
| +++ b/pkg/analyzer/test/src/summary/summary_common.dart |
| @@ -163,6 +163,11 @@ abstract class SummaryTest { |
| LinkedUnit get definingUnit => linked.units[0]; |
| /** |
| + * `true` if element model was used to produce summary. |
| + */ |
| + bool get elementMode; |
|
Paul Berry
2016/05/06 18:11:38
This is redundant. Use `!checkAstDerivedData` ins
scheglov
2016/05/06 19:12:36
Done.
|
| + |
| + /** |
| * `true` if the linked portion of the summary is expected to contain |
| * absolute URIs. This happens because the element model doesn't (yet) store |
| * enough information to recover relative URIs, TODO(paulberry): fix this. |
| @@ -357,7 +362,7 @@ abstract class SummaryTest { |
| */ |
| void checkInferredTypeSlot( |
| int slotId, String absoluteUri, String relativeUri, String expectedName, |
| - {bool allowTypeParameters: false, |
| + {int numTypeArguments: 0, |
| ReferenceKind expectedKind: ReferenceKind.classOrEnum, |
| int expectedTargetUnit: 0, |
| LinkedUnit linkedSourceUnit, |
| @@ -366,7 +371,7 @@ abstract class SummaryTest { |
| bool onlyInStrongMode: true}) { |
| if (strongMode || !onlyInStrongMode) { |
| checkLinkedTypeSlot(slotId, absoluteUri, relativeUri, expectedName, |
| - allowTypeArguments: allowTypeParameters, |
| + numTypeArguments: numTypeArguments, |
| expectedKind: expectedKind, |
| expectedTargetUnit: expectedTargetUnit, |
| linkedSourceUnit: linkedSourceUnit, |
| @@ -400,10 +405,17 @@ abstract class SummaryTest { |
| } |
| /** |
| + * Verify that the given [typeRef] represents the type `dynamic`. |
| + */ |
| + void checkLinkedDynamicTypeRef(EntityRef typeRef) { |
| + checkLinkedTypeRef(typeRef, null, null, 'dynamic'); |
| + } |
| + |
| + /** |
| * Verify that the given [typeRef] represents a reference to a type declared |
| * in a file reachable via [absoluteUri] and [relativeUri], having name |
| - * [expectedName]. If [allowTypeArguments] is true, allow the type |
| - * reference to supply type arguments. [expectedKind] is the kind of object |
| + * [expectedName]. Verify that the number of type arguments |
| + * is equal to [numTypeArguments]. [expectedKind] is the kind of object |
| * referenced. [linkedSourceUnit] and [unlinkedSourceUnit] refer to the |
| * compilation unit within which the [typeRef] appears; if not specified they |
| * are assumed to refer to the defining compilation unit. |
| @@ -414,7 +426,7 @@ abstract class SummaryTest { |
| */ |
| void checkLinkedTypeRef(EntityRef typeRef, String absoluteUri, |
| String relativeUri, String expectedName, |
| - {bool allowTypeArguments: false, |
| + {int numTypeArguments: 0, |
| ReferenceKind expectedKind: ReferenceKind.classOrEnum, |
| int expectedTargetUnit: 0, |
| LinkedUnit linkedSourceUnit, |
| @@ -425,9 +437,7 @@ abstract class SummaryTest { |
| reason: 'No entry in linkedSourceUnit.types matching slotId'); |
| expect(typeRef.paramReference, 0); |
| int index = typeRef.reference; |
| - if (!allowTypeArguments) { |
| - expect(typeRef.typeArguments, isEmpty); |
| - } |
| + expect(typeRef.typeArguments, hasLength(numTypeArguments)); |
| checkReferenceIndex(index, absoluteUri, relativeUri, expectedName, |
| expectedKind: expectedKind, |
| expectedTargetUnit: expectedTargetUnit, |
| @@ -439,8 +449,8 @@ abstract class SummaryTest { |
| /** |
| * Verify that the given [slotId] represents a reference to a type declared |
| * in a file reachable via [absoluteUri] and [relativeUri], having name |
| - * [expectedName]. If [allowTypeArguments] is true, allow the type |
| - * reference to supply type arguments. [expectedKind] is the kind of object |
| + * [expectedName]. Verify that the number of type arguments |
| + * is equal to [numTypeArguments]. [expectedKind] is the kind of object |
| * referenced. [linkedSourceUnit] and [unlinkedSourceUnit] refer to the |
| * compilation unit within which the [typeRef] appears; if not specified they |
| * are assumed to refer to the defining compilation unit. |
| @@ -451,7 +461,7 @@ abstract class SummaryTest { |
| */ |
| void checkLinkedTypeSlot( |
| int slotId, String absoluteUri, String relativeUri, String expectedName, |
| - {bool allowTypeArguments: false, |
| + {int numTypeArguments: 0, |
| ReferenceKind expectedKind: ReferenceKind.classOrEnum, |
| int expectedTargetUnit: 0, |
| LinkedUnit linkedSourceUnit, |
| @@ -468,7 +478,7 @@ abstract class SummaryTest { |
| absoluteUri, |
| relativeUri, |
| expectedName, |
| - allowTypeArguments: allowTypeArguments, |
| + numTypeArguments: numTypeArguments, |
| expectedKind: expectedKind, |
| expectedTargetUnit: expectedTargetUnit, |
| linkedSourceUnit: linkedSourceUnit, |
| @@ -1096,7 +1106,12 @@ class E {} |
| test_class_alias_reference_generic() { |
| EntityRef typeRef = serializeTypeText('C', |
| otherDeclarations: 'class C<D, E> = F with G; class F {} class G {}'); |
| - checkTypeRef(typeRef, null, null, 'C', numTypeParameters: 2); |
| + checkTypeRef(typeRef, null, null, 'C', |
| + numTypeParameters: 2, numTypeArguments: elementMode ? 2 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + checkDynamicTypeRef(typeRef.typeArguments[1]); |
| + } |
| } |
| test_class_alias_reference_generic_imported() { |
| @@ -1105,7 +1120,11 @@ class E {} |
| EntityRef typeRef = |
| serializeTypeText('C', otherDeclarations: 'import "lib.dart";'); |
| checkTypeRef(typeRef, absUri('/lib.dart'), 'lib.dart', 'C', |
| - numTypeParameters: 2); |
| + numTypeParameters: 2, numTypeArguments: elementMode ? 2 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + checkDynamicTypeRef(typeRef.typeArguments[1]); |
| + } |
| } |
| test_class_alias_supertype() { |
| @@ -1318,7 +1337,12 @@ class E {} |
| test_class_reference_generic() { |
| EntityRef typeRef = |
| serializeTypeText('C', otherDeclarations: 'class C<D, E> {}'); |
| - checkTypeRef(typeRef, null, null, 'C', numTypeParameters: 2); |
| + checkTypeRef(typeRef, null, null, 'C', |
| + numTypeParameters: 2, numTypeArguments: elementMode ? 2 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + checkDynamicTypeRef(typeRef.typeArguments[1]); |
| + } |
| } |
| test_class_reference_generic_imported() { |
| @@ -1326,7 +1350,11 @@ class E {} |
| EntityRef typeRef = |
| serializeTypeText('C', otherDeclarations: 'import "lib.dart";'); |
| checkTypeRef(typeRef, absUri('/lib.dart'), 'lib.dart', 'C', |
| - numTypeParameters: 2); |
| + numTypeParameters: 2, numTypeArguments: elementMode ? 2 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + checkDynamicTypeRef(typeRef.typeArguments[1]); |
| + } |
| } |
| test_class_superclass() { |
| @@ -1345,10 +1373,16 @@ class E {} |
| test_class_type_param_bound() { |
| UnlinkedClass cls = serializeClassText('class C<T extends List> {}'); |
| expect(cls.typeParameters, hasLength(1)); |
| - expect(cls.typeParameters[0].name, 'T'); |
| - expect(cls.typeParameters[0].bound, isNotNull); |
| - checkTypeRef(cls.typeParameters[0].bound, 'dart:core', 'dart:core', 'List', |
| - numTypeParameters: 1); |
| + { |
| + UnlinkedTypeParam typeParameter = cls.typeParameters[0]; |
| + expect(typeParameter.name, 'T'); |
| + expect(typeParameter.bound, isNotNull); |
| + checkTypeRef(typeParameter.bound, 'dart:core', 'dart:core', 'List', |
| + numTypeParameters: 1, numTypeArguments: elementMode ? 1 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeParameter.bound.typeArguments[0]); |
| + } |
| + } |
| } |
| test_class_type_param_f_bound() { |
| @@ -7425,7 +7459,12 @@ get f => null;'''; |
| UnlinkedVariable variable = |
| serializeVariableText('import "dart:async" as a; a.Future v;'); |
| checkTypeRef(variable.type, 'dart:async', 'dart:async', 'Future', |
| - expectedPrefix: 'a', numTypeParameters: 1); |
| + expectedPrefix: 'a', |
| + numTypeParameters: 1, |
| + numTypeArguments: elementMode ? 1 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(variable.type.typeArguments[0]); |
| + } |
| } |
| test_import_prefixes_take_precedence_over_imported_names() { |
| @@ -7455,7 +7494,10 @@ D dCls; |
| UnlinkedVariable variable = |
| serializeVariableText('import "dart:async"; Future v;'); |
| checkTypeRef(variable.type, 'dart:async', 'dart:async', 'Future', |
| - numTypeParameters: 1); |
| + numTypeParameters: 1, numTypeArguments: elementMode ? 1 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(variable.type.typeArguments[0]); |
| + } |
| } |
| test_import_reference_merged_no_prefix() { |
| @@ -7466,10 +7508,24 @@ import "dart:async" show Stream; |
| Future f; |
| Stream s; |
| '''); |
| - checkTypeRef(findVariable('f').type, 'dart:async', 'dart:async', 'Future', |
| - numTypeParameters: 1); |
| - checkTypeRef(findVariable('s').type, 'dart:async', 'dart:async', 'Stream', |
| - expectedTargetUnit: 1, numTypeParameters: 1); |
| + { |
| + EntityRef typeRef = findVariable('f').type; |
| + checkTypeRef(typeRef, 'dart:async', 'dart:async', 'Future', |
| + numTypeParameters: 1, numTypeArguments: elementMode ? 1 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + } |
| + } |
| + { |
| + EntityRef typeRef = findVariable('s').type; |
| + checkTypeRef(typeRef, 'dart:async', 'dart:async', 'Stream', |
| + expectedTargetUnit: 1, |
| + numTypeParameters: 1, |
| + numTypeArguments: elementMode ? 1 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + } |
| + } |
| } |
| test_import_reference_merged_prefixed() { |
| @@ -7480,10 +7536,27 @@ import "dart:async" as a show Stream; |
| a.Future f; |
| a.Stream s; |
| '''); |
| - checkTypeRef(findVariable('f').type, 'dart:async', 'dart:async', 'Future', |
| - expectedPrefix: 'a', numTypeParameters: 1); |
| - checkTypeRef(findVariable('s').type, 'dart:async', 'dart:async', 'Stream', |
| - expectedTargetUnit: 1, expectedPrefix: 'a', numTypeParameters: 1); |
| + { |
| + EntityRef typeRef = findVariable('f').type; |
| + checkTypeRef(typeRef, 'dart:async', 'dart:async', 'Future', |
| + expectedPrefix: 'a', |
| + numTypeParameters: 1, |
| + numTypeArguments: elementMode ? 1 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + } |
| + } |
| + { |
| + EntityRef typeRef = findVariable('s').type; |
| + checkTypeRef(typeRef, 'dart:async', 'dart:async', 'Stream', |
| + expectedTargetUnit: 1, |
| + expectedPrefix: 'a', |
| + numTypeParameters: 1, |
| + numTypeArguments: elementMode ? 1 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + } |
| + } |
| } |
| test_import_reference_merged_prefixed_separate_libraries() { |
| @@ -7558,8 +7631,7 @@ class D extends p.C {} // Prevent "unused import" warning |
| EntityRef type = getTypeRefForSlot(cls.fields[0].inferredTypeSlot); |
| // Check that x has inferred type `Map<dynamic, int>`. |
| checkLinkedTypeRef(type, 'dart:core', 'dart:core', 'Map', |
| - allowTypeArguments: true, numTypeParameters: 2); |
| - expect(type.typeArguments, hasLength(2)); |
| + numTypeParameters: 2, numTypeArguments: 2); |
| checkLinkedTypeRef(type.typeArguments[0], null, null, 'dynamic'); |
| checkLinkedTypeRef(type.typeArguments[1], 'dart:core', 'dart:core', 'int'); |
| } |
| @@ -7575,7 +7647,7 @@ class D extends p.C {} // Prevent "unused import" warning |
| EntityRef type = getTypeRefForSlot(cls.fields[0].inferredTypeSlot); |
| // Check that v has inferred type Map<T, int>. |
| checkLinkedTypeRef(type, 'dart:core', 'dart:core', 'Map', |
| - allowTypeArguments: true, numTypeParameters: 2); |
| + numTypeParameters: 2, numTypeArguments: 2); |
| checkParamTypeRef(type.typeArguments[0], 1); |
| checkLinkedTypeRef(type.typeArguments[1], 'dart:core', 'dart:core', 'int'); |
| } |
| @@ -7728,12 +7800,11 @@ var v = h((y) {}); |
| UnlinkedClass cls = |
| serializeClassText('class C { final x = <int, dynamic>{}; }'); |
| EntityRef type = getTypeRefForSlot(cls.fields[0].inferredTypeSlot); |
| - // Check that x has inferred type `Map<int>`. The trailing type argument |
| - // `dynamic` is omitted. |
| + // Check that x has inferred type `Map<int, dynamic>`. |
| checkLinkedTypeRef(type, 'dart:core', 'dart:core', 'Map', |
| - allowTypeArguments: true, numTypeParameters: 2); |
| - expect(type.typeArguments, hasLength(1)); |
| + numTypeParameters: 2, numTypeArguments: 2); |
| checkLinkedTypeRef(type.typeArguments[0], 'dart:core', 'dart:core', 'int'); |
| + checkLinkedDynamicTypeRef(type.typeArguments[1]); |
| } |
| test_inferred_type_skips_unnecessary_dynamic() { |
| @@ -7742,9 +7813,9 @@ var v = h((y) {}); |
| } |
| UnlinkedClass cls = serializeClassText('class C { final x = []; }'); |
| EntityRef type = getTypeRefForSlot(cls.fields[0].inferredTypeSlot); |
| - // Check that x has inferred type `List`, not `List<dynamic>`. |
| + // Check that x has inferred type `List<dynamic>`. |
| checkLinkedTypeRef(type, 'dart:core', 'dart:core', 'List', |
| - numTypeParameters: 1); |
| + numTypeParameters: 1, numTypeArguments: 1); |
| } |
| test_initializer_executable_with_bottom_return_type() { |
| @@ -8878,31 +8949,28 @@ bool f() => true; |
| EntityRef typeRef = serializeTypeText('List<int>'); |
| checkTypeRef(typeRef, 'dart:core', 'dart:core', 'List', |
| numTypeParameters: 1, numTypeArguments: 1); |
| - expect(typeRef.typeArguments, hasLength(1)); |
| checkTypeRef(typeRef.typeArguments[0], 'dart:core', 'dart:core', 'int'); |
| } |
| test_type_arguments_explicit_dynamic() { |
| EntityRef typeRef = serializeTypeText('List<dynamic>'); |
| checkTypeRef(typeRef, 'dart:core', 'dart:core', 'List', |
| - numTypeParameters: 1); |
| - expect(typeRef.typeArguments, isEmpty); |
| + numTypeParameters: 1, numTypeArguments: 1); |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| } |
| test_type_arguments_explicit_dynamic_dynamic() { |
| EntityRef typeRef = serializeTypeText('Map<dynamic, dynamic>'); |
| checkTypeRef(typeRef, 'dart:core', 'dart:core', 'Map', |
| - numTypeParameters: 2); |
| - // Trailing type arguments of type `dynamic` are omitted. |
| - expect(typeRef.typeArguments, isEmpty); |
| + numTypeParameters: 2, numTypeArguments: 2); |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + checkDynamicTypeRef(typeRef.typeArguments[1]); |
| } |
| test_type_arguments_explicit_dynamic_int() { |
| EntityRef typeRef = serializeTypeText('Map<dynamic, int>'); |
| checkTypeRef(typeRef, 'dart:core', 'dart:core', 'Map', |
| numTypeParameters: 2, numTypeArguments: 2); |
| - // Leading type arguments of type `dynamic` are not omitted. |
| - expect(typeRef.typeArguments.length, 2); |
| checkDynamicTypeRef(typeRef.typeArguments[0]); |
| checkTypeRef(typeRef.typeArguments[1], 'dart:core', 'dart:core', 'int'); |
| } |
| @@ -8913,24 +8981,22 @@ bool f() => true; |
| checkTypeRef(typeRef, null, null, 'F', |
| expectedKind: ReferenceKind.typedef, |
| numTypeParameters: 1, |
| - numTypeArguments: 0); |
| - expect(typeRef.typeArguments, isEmpty); |
| + numTypeArguments: 1); |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| } |
| test_type_arguments_explicit_String_dynamic() { |
| EntityRef typeRef = serializeTypeText('Map<String, dynamic>'); |
| checkTypeRef(typeRef, 'dart:core', 'dart:core', 'Map', |
| - numTypeParameters: 2, numTypeArguments: 1); |
| - // Trailing type arguments of type `dynamic` are omitted. |
| - expect(typeRef.typeArguments.length, 1); |
| + numTypeParameters: 2, numTypeArguments: 2); |
| checkTypeRef(typeRef.typeArguments[0], 'dart:core', 'dart:core', 'String'); |
| + checkDynamicTypeRef(typeRef.typeArguments[1]); |
| } |
| test_type_arguments_explicit_String_int() { |
| EntityRef typeRef = serializeTypeText('Map<String, int>'); |
| checkTypeRef(typeRef, 'dart:core', 'dart:core', 'Map', |
| numTypeParameters: 2, numTypeArguments: 2); |
| - expect(typeRef.typeArguments.length, 2); |
| checkTypeRef(typeRef.typeArguments[0], 'dart:core', 'dart:core', 'String'); |
| checkTypeRef(typeRef.typeArguments[1], 'dart:core', 'dart:core', 'int'); |
| } |
| @@ -8942,15 +9008,16 @@ bool f() => true; |
| expectedKind: ReferenceKind.typedef, |
| numTypeParameters: 1, |
| numTypeArguments: 1); |
| - expect(typeRef.typeArguments, hasLength(1)); |
| checkTypeRef(typeRef.typeArguments[0], 'dart:core', 'dart:core', 'int'); |
| } |
| test_type_arguments_implicit() { |
| EntityRef typeRef = serializeTypeText('List'); |
| checkTypeRef(typeRef, 'dart:core', 'dart:core', 'List', |
| - numTypeParameters: 1); |
| - expect(typeRef.typeArguments, isEmpty); |
| + numTypeParameters: 1, numTypeArguments: elementMode ? 1 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + } |
| } |
| test_type_arguments_implicit_typedef() { |
| @@ -8959,15 +9026,32 @@ bool f() => true; |
| checkTypeRef(typeRef, null, null, 'F', |
| expectedKind: ReferenceKind.typedef, |
| numTypeParameters: 1, |
| - numTypeArguments: 0); |
| - expect(typeRef.typeArguments, isEmpty); |
| + numTypeArguments: elementMode ? 1 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + } |
| + } |
| + |
| + test_type_arguments_implicit_typedef_withBound() { |
| + EntityRef typeRef = serializeTypeText('F', |
| + otherDeclarations: 'typedef T F<T extends num>();'); |
| + checkTypeRef(typeRef, null, null, 'F', |
| + expectedKind: ReferenceKind.typedef, |
| + numTypeParameters: 1, |
| + numTypeArguments: elementMode ? 1 : 0); |
| + if (elementMode) { |
| + if (strongMode) { |
| + checkTypeRef(typeRef.typeArguments[0], 'dart:core', 'dart:core', 'num'); |
| + } else { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + } |
| + } |
| } |
| test_type_arguments_order() { |
| EntityRef typeRef = serializeTypeText('Map<int, Object>'); |
| checkTypeRef(typeRef, 'dart:core', 'dart:core', 'Map', |
| numTypeParameters: 2, numTypeArguments: 2); |
| - expect(typeRef.typeArguments, hasLength(2)); |
| checkTypeRef(typeRef.typeArguments[0], 'dart:core', 'dart:core', 'int'); |
| checkTypeRef(typeRef.typeArguments[1], 'dart:core', 'dart:core', 'Object'); |
| } |
| @@ -9291,7 +9375,13 @@ typedef F();'''; |
| EntityRef typeRef = |
| serializeTypeText('F', otherDeclarations: 'typedef void F<A, B>();'); |
| checkTypeRef(typeRef, null, null, 'F', |
| - numTypeParameters: 2, expectedKind: ReferenceKind.typedef); |
| + numTypeParameters: 2, |
| + expectedKind: ReferenceKind.typedef, |
| + numTypeArguments: elementMode ? 2 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + checkDynamicTypeRef(typeRef.typeArguments[1]); |
| + } |
| } |
| test_typedef_reference_generic_imported() { |
| @@ -9299,7 +9389,13 @@ typedef F();'''; |
| EntityRef typeRef = |
| serializeTypeText('F', otherDeclarations: 'import "lib.dart";'); |
| checkTypeRef(typeRef, absUri('/lib.dart'), 'lib.dart', 'F', |
| - numTypeParameters: 2, expectedKind: ReferenceKind.typedef); |
| + numTypeParameters: 2, |
| + expectedKind: ReferenceKind.typedef, |
| + numTypeArguments: elementMode ? 2 : 0); |
| + if (elementMode) { |
| + checkDynamicTypeRef(typeRef.typeArguments[0]); |
| + checkDynamicTypeRef(typeRef.typeArguments[1]); |
| + } |
| } |
| test_typedef_return_type_explicit() { |
| @@ -9587,9 +9683,9 @@ var v;'''; |
| UnlinkedVariable v = serializeVariableText('final v = <int, dynamic>{};'); |
| EntityRef type = getTypeRefForSlot(v.propagatedTypeSlot); |
| checkLinkedTypeRef(type, 'dart:core', 'dart:core', 'Map', |
| - allowTypeArguments: true, numTypeParameters: 2); |
| - expect(type.typeArguments, hasLength(1)); |
| + numTypeParameters: 2, numTypeArguments: 2); |
| checkLinkedTypeRef(type.typeArguments[0], 'dart:core', 'dart:core', 'int'); |
| + checkLinkedDynamicTypeRef(type.typeArguments[1]); |
| } |
| test_variable_propagatedTypeSlot_const() { |