diff --git a/test/end2end/model_special_cases_test.dart b/test/end2end/model_special_cases_test.dart index a826434cf1..8f36898eb2 100644 --- a/test/end2end/model_special_cases_test.dart +++ b/test/end2end/model_special_cases_test.dart @@ -78,10 +78,60 @@ void main() { VersionRange(min: Version.parse('2.13.0-0'), includeMin: true); final _genericMetadataAllowed = VersionRange(min: Version.parse('2.13.0-0'), includeMin: true); + final _tripleShiftAllowed = + VersionRange(min: Version.parse('2.13.0-0'), includeMin: true); // Experimental features not yet enabled by default. Move tests out of this // block when the feature is enabled by default. group('Experiments', () { + group('triple-shift', () { + Library tripleShift; + Class C, E, F; + Extension ShiftIt; + Operator classShift, extensionShift; + Field constantTripleShifted; + + setUpAll(() async { + tripleShift = (await _testPackageGraphExperiments) + .libraries + .firstWhere((l) => l.name == 'triple_shift'); + C = tripleShift.classes.firstWhere((c) => c.name == 'C'); + E = tripleShift.classes.firstWhere((c) => c.name == 'E'); + F = tripleShift.classes.firstWhere((c) => c.name == 'F'); + ShiftIt = tripleShift.extensions.firstWhere((e) => e.name == 'ShiftIt'); + classShift = + C.instanceOperators.firstWhere((o) => o.name.contains('>>>')); + extensionShift = + ShiftIt.instanceOperators.firstWhere((o) => o.name.contains('>>>')); + constantTripleShifted = C.constantFields + .firstWhere((f) => f.name == 'constantTripleShifted'); + }); + + test('constants with triple shift render correctly', () { + expect(constantTripleShifted.constantValue, equals('3 >>> 5')); + }); + + test('operators exist and are named correctly', () { + expect(classShift.name, equals('operator >>>')); + expect(extensionShift.name, equals('operator >>>')); + }); + + test( + 'inheritance and overriding of triple shift operators works correctly', + () { + var tripleShiftE = + E.instanceOperators.firstWhere((o) => o.name.contains('>>>')); + var tripleShiftF = + F.instanceOperators.firstWhere((o) => o.name.contains('>>>')); + + expect(tripleShiftE.isInherited, isTrue); + expect(tripleShiftE.canonicalModelElement, equals(classShift)); + expect(tripleShiftE.modelType.returnType.name, equals('C')); + expect(tripleShiftF.isInherited, isFalse); + expect(tripleShiftF.modelType.returnType.name, equals('F')); + }); + }, skip: !_tripleShiftAllowed.allows(_platformVersion)); + group('generic metadata', () { Library genericMetadata; TopLevelVariable f; diff --git a/testing/test_package_experiments/analysis_options.yaml b/testing/test_package_experiments/analysis_options.yaml index 69c1a19046..620d59460b 100644 --- a/testing/test_package_experiments/analysis_options.yaml +++ b/testing/test_package_experiments/analysis_options.yaml @@ -3,4 +3,5 @@ analyzer: enable-experiment: - non-nullable - nonfunction-type-aliases + - triple-shift - generic-metadata diff --git a/testing/test_package_experiments/lib/generic_functions_as_type_args.dart b/testing/test_package_experiments/lib/generic_functions_as_type_args.dart new file mode 100644 index 0000000000..c7801a4db0 --- /dev/null +++ b/testing/test_package_experiments/lib/generic_functions_as_type_args.dart @@ -0,0 +1,48 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// Tests for verifying generic functions as type arguments. +library generic_function_type_args; + +late List(T)> idFunctions; +late S Function(T)>(S) ff; + +typedef F = T Function(T); + +class C { + final T value; + const C(this.value); +} + +T f(T value) => value; + +extension E on T { + T get extensionValue => this; +} + +// A generic function type can be a type parameter bound. + +// For a type alias: +typedef FB = S Function(S); + +// For a class: +class CB> { + final T function; + const CB(this.function); +} + +// For a function: +T fb(T value) => value; + +extension EB on T { + T get boundExtensionValue => this; + + // Any function type has a `call` of its own type? + T get boundCall => this.call; +} + +// Can be used as arguments to metadata too. +@C(f) +@CB>(fb) +void main() {} \ No newline at end of file diff --git a/testing/test_package_experiments/lib/triple_shift.dart b/testing/test_package_experiments/lib/triple_shift.dart new file mode 100644 index 0000000000..bbcb26318b --- /dev/null +++ b/testing/test_package_experiments/lib/triple_shift.dart @@ -0,0 +1,42 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// Tests for verifying triple_shift operator, borrowed from the SDK. +library triple_shift; + +class C { + static int ctr = 0; + + /// Check that constants using a triple shift operator appear correctly. + static const int constantTripleShifted = 3>>>5; + final Object? _text; + C([Object? text]) : _text = text ?? "${++ctr}"; + + // It's possible to declare a `>>>` operator. + C operator >>>(arg) => C("(${++ctr}:$_text>>>$arg)"); + + // + binds more strongly than `>>`, `>>>` and `<<`. + C operator +(arg) => C("(${++ctr}:$_text+$arg)"); + // Both `>>` and `<<` binds exactly as strongly as `>>>`. + C operator >>(arg) => C("(${++ctr}:$_text>>$arg)"); + C operator <<(arg) => C("(${++ctr}:$_text<<$arg)"); + // & binds less strongly than `>>`, `>>>` and `<<`. + C operator &(arg) => C("(${++ctr}:$_text&$arg)"); + + String toString() => "${_text}"; +} + +class _D extends C {} + +class E extends _D {} + +class F extends E { + @override + F operator >>>(arg) => F(); +} + +// Valid in extensions too. +extension ShiftIt on T { + List operator >>>(int count) => List.filled(count, this); +} \ No newline at end of file