Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) 2025, 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.

/// @assertion A postfix expression expression can follow a `?` in a conditional
/// expression, as in `{e1 ? . id : e2}`. This is not ambiguous with `e1?.id`
/// since we parse `?.` as a single token, and will keep doing so. It does mean
/// that `{e1?.id:e2}` and `{e1? .id:e2}` will now both be valid and have
/// different meanings, where the existing grammar didn’t allow the `?` token to
/// be followed by `.` anywhere.
///
/// @description Checks that `{e1?.id:e2}` is parsed like a map literal.
/// @author sgrekhov22@gmail.com

// SharedOptions=--enable-experiment=enum-shorthands

import '../../Utils/expect.dart';

class C {
static C id = C();
}

extension on bool {
int get id => 42;
}

main() {
bool e1 = 2 > 1; // true
var e2 = "value";
Object o = C();
if (o is C) {
o = {e1?.id: e2}; // ignore: invalid_null_aware_operator
Expect.isTrue(o is Map);
Expect.mapEquals({42: "value"}, o);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2025, 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.

/// @assertion A postfix expression expression can follow a `?` in a conditional
/// expression, as in `{e1 ? . id : e2}`. This is not ambiguous with `e1?.id`
/// since we parse `?.` as a single token, and will keep doing so. It does mean
/// that `{e1?.id:e2}` and `{e1? .id:e2}` will now both be valid and have
/// different meanings, where the existing grammar didn’t allow the `?` token to
/// be followed by `.` anywhere.
///
/// @description Checks that `{e1? .id:e2}` is parsed like a set literal.
/// @author sgrekhov22@gmail.com

// SharedOptions=--enable-experiment=enum-shorthands

import '../../Utils/expect.dart';

class C {
int value;
C(this.value);
static C id = C(0);
}

extension on bool {
int get id => 42;
}

main() {
bool e1 = 2 > 1; // true
var e2 = "value";
Object o = <C>{C(1)};
if (o is Set<C>) {
o = {e1? .id: e2};
Expect.isTrue(o is Set); // ignore: unnecessary_type_check
Expect.setEquals({C.id}, o as Set);
}
}
101 changes: 101 additions & 0 deletions LanguageFeatures/Static-access-shorthand/non_ambiguity_A02_t01.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) 2025, 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.

/// @assertion A postfix expression expression can follow a `?` in a conditional
/// expression, as in `{e1 ? . id : e2}`. This is not ambiguous with `e1?.id`
/// since we parse `?.` as a single token, and will keep doing so. It does mean
/// that `{e1?.id:e2}` and `{e1? .id:e2}` will now both be valid and have
/// different meanings, where the existing grammar didn’t allow the `?` token to
/// be followed by `.` anywhere.
///
/// @description Checks that for null-aware elements `? .id` is parsed as
/// `? C.id`.
/// @author sgrekhov22@gmail.com

// SharedOptions=--enable-experiment=enum-shorthands,null-aware-elements

import '../../Utils/expect.dart';

class C {
int value;
C(this.value);
C.id(this.value);
static C? one = C(1);
static C? get two => C(2);
static C? three() => C(3);

@override
bool operator ==(Object other) {
if (other is C) {
return value == other.value;
}
return false;
}

@override
int get hashCode => value.hashCode;
}

mixin M on C {
static M? one = MC(1);
static M? get two => MC(2);
static M? three() => MC(3);
}

class MC = C with M;

enum E {
e1, e2, e3;
static E? one = E.e1;
static E? get two => E.e2;
static E? three() => E.e3;
}

extension type ET(int value) {
ET.id(this.value);
static ET? one = ET(1);
static ET? get two => ET(2);
static ET? three() => ET(3);
}

main() {
var l1 = <C>[
? .id(0),
? .new(0),
? .one,
? .two,
? .three()
];
Expect.listEquals([C(0), C(0), C(1), C(2), C(3)], l1);

var s = <M>{
? .one,
? .two,
? .three()
};
Expect.setEquals({MC(1), MC(2), MC(3)}, s);

var m1 = <String, E>{
"key0": ? .e1,
"key1": ? .one,
"key2": ? .two,
"key3": ? .three()
};
Expect.mapEquals({"key0": E.e1, "key1": E.e1, "key2": E.e2, "key3":E.e3}, m1);

var m2 = <ET, String> {
? .id(-1): "value0",
? .new(0): "value1",
? .one: "value2",
? .two: "value3",
? .three(): "value4"
};
Expect.mapEquals({
ET(-1): "value0",
ET(0): "value1",
ET(1): "value2",
ET(2): "value3",
ET(3): "value4"
}, m2);
}
220 changes: 220 additions & 0 deletions LanguageFeatures/Static-access-shorthand/non_ambiguity_A02_t02.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// Copyright (c) 2025, 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.

/// @assertion A postfix expression expression can follow a `?` in a conditional
/// expression, as in `{e1 ? . id : e2}`. This is not ambiguous with `e1?.id`
/// since we parse `?.` as a single token, and will keep doing so. It does mean
/// that `{e1?.id:e2}` and `{e1? .id:e2}` will now both be valid and have
/// different meanings, where the existing grammar didn’t allow the `?` token to
/// be followed by `.` anywhere.
///
/// @description Checks that `?.id` is parsed as `?. id` which is a compile-time
/// error.
/// @author sgrekhov22@gmail.com

// SharedOptions=--enable-experiment=enum-shorthands,null-aware-elements

class C {
int value;
C(this.value);
C.id(this.value);
static C? one = C(1);
static C? get two => C(2);
static C? three() => C(3);

@override
bool operator ==(Object other) {
if (other is C) {
return value == other.value;
}
return false;
}

@override
int get hashCode => value.hashCode;
}

mixin M on C {
static M? one = MC(1);
static M? get two => MC(2);
static M? three() => MC(3);
}

class MC = C with M;

enum E {
e1, e2, e3;
static E? one = E.e1;
static E? get two => E.e2;
static E? three() => E.e3;
}

extension type ET(int value) {
ET.id(this.value);
static ET? one = ET(1);
static ET? get two => ET(2);
static ET? three() => ET(3);
}

void testConstructors() {
var l = <C>[
?.id(0)
// ^^
// [analyzer] unspecified
// [cfe] unspecified
];

var s = <C>{
?.new(0),
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};

var m1 = <String, ET> {
"key": ?.id(0)
// ^^
// [analyzer] unspecified
// [cfe] unspecified
?.new(0): "value1",
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};

var m2 = <ET, String> {
?.new(0): "value"
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};
}

void testVariables() {
var l = <C>[
?.one
// ^^
// [analyzer] unspecified
// [cfe] unspecified
];

var s = <M>{
?.one
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};

var m1 = <String, E>{
"key": ?.one,
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};

var m2 = <ET, String> {
?.one: "value"
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};
}

void testGetters() {
var l = <C>[
?.two
// ^^
// [analyzer] unspecified
// [cfe] unspecified
];

var s = <M>{
?.two
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};

var m1 = <String, E>{
"key": ?.two,
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};

var m2 = <ET, String> {
?.two: "value"
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};
}

void testMethods() {
var l = <C>[
?.three()
// ^^
// [analyzer] unspecified
// [cfe] unspecified
];

var s = <M>{
?.three()
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};

var m1 = <String, E>{
"key": ?.three(),
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};

var m2 = <ET, String> {
?.three(): "value"
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};
}

void testEnumValues() {
var l = <E>[
?.e0
// ^^
// [analyzer] unspecified
// [cfe] unspecified
];

var s = <E>{
?.e0
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};

var m1 = <String, E>{
"key": ?.e0,
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};

var m2 = <E, String> {
?.e0: "value"
// ^^
// [analyzer] unspecified
// [cfe] unspecified
};
}

main() {
testConstructors();
testVariables();
testGetters();
testMetods();
testEnumValues();
}
Loading
Loading