Skip to content

Commit 2bc2f89

Browse files
chloestefantsovaCommit Queue
authored andcommitted
[analyzer][null-aware-elements] Add NullAwareElement AST node
Closes #56267 Part of #56266 Part of #55949 Change-Id: I7c79d6be312f579e95291f2c2d21e3f996500b87 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/376101 Reviewed-by: Keerti Parthasarathy <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent 90e4602 commit 2bc2f89

File tree

18 files changed

+346
-5
lines changed

18 files changed

+346
-5
lines changed

pkg/analyzer/lib/dart/ast/ast.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ export 'package:analyzer/src/dart/ast/ast.dart'
180180
NodeList,
181181
NormalFormalParameter,
182182
NullAssertPattern,
183+
NullAwareElement,
183184
NullCheckPattern,
184185
NullLiteral,
185186
NullShortableExpression,

pkg/analyzer/lib/dart/ast/visitor.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,10 @@ class GeneralizingAstVisitor<R> implements AstVisitor<R> {
559559
@override
560560
R? visitNullAssertPattern(NullAssertPattern node) => visitDartPattern(node);
561561

562+
@override
563+
R? visitNullAwareElement(NullAwareElement node) =>
564+
visitCollectionElement(node);
565+
562566
@override
563567
R? visitNullCheckPattern(NullCheckPattern node) => visitDartPattern(node);
564568

@@ -1471,6 +1475,12 @@ class RecursiveAstVisitor<R> implements AstVisitor<R> {
14711475
return null;
14721476
}
14731477

1478+
@override
1479+
R? visitNullAwareElement(NullAwareElement node) {
1480+
node.visitChildren(this);
1481+
return null;
1482+
}
1483+
14741484
@override
14751485
R? visitNullCheckPattern(NullCheckPattern node) {
14761486
node.visitChildren(this);
@@ -2209,6 +2219,9 @@ class SimpleAstVisitor<R> implements AstVisitor<R> {
22092219
@override
22102220
R? visitNullAssertPattern(NullAssertPattern node) => null;
22112221

2222+
@override
2223+
R? visitNullAwareElement(NullAwareElement node) => null;
2224+
22122225
@override
22132226
R? visitNullCheckPattern(NullCheckPattern node) => null;
22142227

@@ -2770,6 +2783,9 @@ class ThrowingAstVisitor<R> implements AstVisitor<R> {
27702783
@override
27712784
R? visitNullAssertPattern(NullAssertPattern node) => _throw(node);
27722785

2786+
@override
2787+
R? visitNullAwareElement(NullAwareElement node) => _throw(node);
2788+
27732789
@override
27742790
R? visitNullCheckPattern(NullCheckPattern node) => _throw(node);
27752791

@@ -3880,6 +3896,14 @@ class TimedAstVisitor<T> implements AstVisitor<T> {
38803896
return result;
38813897
}
38823898

3899+
@override
3900+
T? visitNullAwareElement(NullAwareElement node) {
3901+
stopwatch.start();
3902+
T? result = _baseVisitor.visitNullAwareElement(node);
3903+
stopwatch.stop();
3904+
return result;
3905+
}
3906+
38833907
@override
38843908
T? visitNullCheckPattern(NullCheckPattern node) {
38853909
stopwatch.start();
@@ -4772,6 +4796,9 @@ class UnifyingAstVisitor<R> implements AstVisitor<R> {
47724796
@override
47734797
R? visitNullAssertPattern(NullAssertPattern node) => visitNode(node);
47744798

4799+
@override
4800+
R? visitNullAwareElement(NullAwareElement node) => visitNode(node);
4801+
47754802
@override
47764803
R? visitNullCheckPattern(NullCheckPattern node) => visitNode(node);
47774804

pkg/analyzer/lib/src/dart/ast/ast.dart

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,8 @@ abstract class AstVisitor<R> {
14421442

14431443
R? visitNullAssertPattern(NullAssertPattern node);
14441444

1445+
R? visitNullAwareElement(NullAwareElement node);
1446+
14451447
R? visitNullCheckPattern(NullCheckPattern node);
14461448

14471449
R? visitNullLiteral(NullLiteral node);
@@ -11268,31 +11270,47 @@ final class LogicalOrPatternImpl extends DartPatternImpl
1126811270
/// A single key/value pair in a map literal.
1126911271
///
1127011272
/// mapLiteralEntry ::=
11271-
/// [Expression] ':' [Expression]
11273+
/// '?'? [Expression] ':' '?'? [Expression]
1127211274
abstract final class MapLiteralEntry implements CollectionElement {
1127311275
/// The expression computing the key with which the value is associated.
1127411276
Expression get key;
1127511277

11278+
/// The question prefix for the key that may present in null-aware map
11279+
/// entries.
11280+
Token? get keyQuestion;
11281+
1127611282
/// The colon that separates the key from the value.
1127711283
Token get separator;
1127811284

1127911285
/// The expression computing the value that is associated with the key.
1128011286
Expression get value;
11287+
11288+
/// The question prefix for the value that may present in null-aware map
11289+
/// entries.
11290+
Token? get valueQuestion;
1128111291
}
1128211292

1128311293
final class MapLiteralEntryImpl extends CollectionElementImpl
1128411294
implements MapLiteralEntry {
11295+
@override
11296+
final Token? keyQuestion;
11297+
1128511298
ExpressionImpl _key;
1128611299

1128711300
@override
1128811301
final Token separator;
1128911302

11303+
@override
11304+
final Token? valueQuestion;
11305+
1129011306
ExpressionImpl _value;
1129111307

1129211308
/// Initializes a newly created map literal entry.
1129311309
MapLiteralEntryImpl({
11310+
required this.keyQuestion,
1129411311
required ExpressionImpl key,
1129511312
required this.separator,
11313+
required this.valueQuestion,
1129611314
required ExpressionImpl value,
1129711315
}) : _key = key,
1129811316
_value = value {
@@ -11301,7 +11319,7 @@ final class MapLiteralEntryImpl extends CollectionElementImpl
1130111319
}
1130211320

1130311321
@override
11304-
Token get beginToken => _key.beginToken;
11322+
Token get beginToken => keyQuestion ?? _key.beginToken;
1130511323

1130611324
@override
1130711325
Token get endToken => _value.endToken;
@@ -11322,8 +11340,10 @@ final class MapLiteralEntryImpl extends CollectionElementImpl
1132211340

1132311341
@override
1132411342
ChildEntities get _childEntities => ChildEntities()
11343+
..addToken('keyQuestion', keyQuestion)
1132511344
..addNode('key', key)
1132611345
..addToken('separator', separator)
11346+
..addToken('valueQuestion', valueQuestion)
1132711347
..addNode('value', value);
1132811348

1132911349
@override
@@ -12819,6 +12839,66 @@ final class NullAssertPatternImpl extends DartPatternImpl
1281912839
}
1282012840
}
1282112841

12842+
/// A null-aware element in a list or set literal.
12843+
///
12844+
/// <nullAwareExpressionElement> ::= '?' <expression>
12845+
abstract final class NullAwareElement implements CollectionElement {
12846+
/// The question mark before the expression.
12847+
Token get question;
12848+
12849+
/// The expression computing the value that is associated with the element.
12850+
Expression get value;
12851+
}
12852+
12853+
final class NullAwareElementImpl extends CollectionElementImpl
12854+
implements NullAwareElement {
12855+
@override
12856+
final Token question;
12857+
12858+
ExpressionImpl _value;
12859+
12860+
/// Initializes a newly created null-aware element.
12861+
NullAwareElementImpl({
12862+
required this.question,
12863+
required ExpressionImpl value,
12864+
}) : _value = value {
12865+
_becomeParentOf(_value);
12866+
}
12867+
12868+
@override
12869+
Token get beginToken => question;
12870+
12871+
@override
12872+
Token get endToken => _value.endToken;
12873+
12874+
@override
12875+
ExpressionImpl get value => _value;
12876+
12877+
set value(ExpressionImpl expression) {
12878+
_value = _becomeParentOf(expression);
12879+
}
12880+
12881+
@override
12882+
ChildEntities get _childEntities => ChildEntities()
12883+
..addToken('question', question)
12884+
..addNode('value', value);
12885+
12886+
@override
12887+
E? accept<E>(AstVisitor<E> visitor) => visitor.visitNullAwareElement(this);
12888+
12889+
@override
12890+
void resolveElement(
12891+
ResolverVisitor resolver, CollectionLiteralContext? context) {
12892+
// resolver.visitNullAwareElement(this, context: context);
12893+
resolver.pushRewrite(null);
12894+
}
12895+
12896+
@override
12897+
void visitChildren(AstVisitor visitor) {
12898+
_value.accept(visitor);
12899+
}
12900+
}
12901+
1282212902
/// A null-check pattern.
1282312903
///
1282412904
/// nullCheckPattern ::=

pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,12 @@ class ToSourceVisitor implements AstVisitor<void> {
984984
sink.write(node.operator.lexeme);
985985
}
986986

987+
@override
988+
void visitNullAwareElement(NullAwareElement node) {
989+
sink.write(node.question.lexeme);
990+
_visitNode(node.value);
991+
}
992+
987993
@override
988994
void visitNullCheckPattern(NullCheckPattern node) {
989995
_visitNode(node.pattern);

pkg/analyzer/lib/src/dart/ast/utilities.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,13 @@ class AstComparator implements AstVisitor<bool> {
11141114
isEqualTokens(node.operator, other.operator);
11151115
}
11161116

1117+
@override
1118+
bool visitNullAwareElement(NullAwareElement node) {
1119+
NullAwareElement other = _other as NullAwareElement;
1120+
return isEqualTokens(node.question, other.question) &&
1121+
isEqualNodes(node.value, other.value);
1122+
}
1123+
11171124
@override
11181125
bool visitNullCheckPattern(NullCheckPattern node) {
11191126
var other = _other as NullCheckPattern;
@@ -3042,6 +3049,15 @@ class NodeReplacer extends ThrowingAstVisitor<bool> {
30423049
return visitNode(node);
30433050
}
30443051

3052+
@override
3053+
bool visitNullAwareElement(NullAwareElement node) {
3054+
if (identical(node.value, _oldNode)) {
3055+
(node as NullAwareElementImpl).value = _newNode as ExpressionImpl;
3056+
return true;
3057+
}
3058+
return visitNode(node);
3059+
}
3060+
30453061
@override
30463062
bool visitNullLiteral(NullLiteral node) => visitNode(node);
30473063

pkg/analyzer/lib/src/dart/constant/evaluation.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,24 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
14401440
}
14411441
list.addAll(listValue);
14421442
}
1443+
case NullAwareElement():
1444+
var value = evaluateConstant(element.value);
1445+
switch (value) {
1446+
case InvalidConstant():
1447+
return value;
1448+
case DartObjectImpl():
1449+
if (value.isNull) {
1450+
continue;
1451+
}
1452+
var result = _buildListConstant(
1453+
list,
1454+
[element.value],
1455+
typeSystem,
1456+
listType,
1457+
elementType,
1458+
);
1459+
return result;
1460+
}
14431461
}
14441462
}
14451463

@@ -1530,6 +1548,11 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
15301548
}
15311549
map.addAll(mapValue);
15321550
}
1551+
case NullAwareElement():
1552+
// TODO(cstefantsova): Should it rather be its own code, for example,
1553+
// `CompileTimeErrorCode.NULL_AWARE_ELEMENT_IN_MAP`?
1554+
return InvalidConstant.forEntity(
1555+
element, CompileTimeErrorCode.EXPRESSION_IN_MAP);
15331556
}
15341557
}
15351558

@@ -1607,6 +1630,19 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
16071630
}
16081631
set.addAll(setValue);
16091632
}
1633+
case NullAwareElement():
1634+
var value = evaluateConstant(element.value);
1635+
switch (value) {
1636+
case InvalidConstant():
1637+
return value;
1638+
case DartObjectImpl():
1639+
if (value.isNull) {
1640+
continue;
1641+
}
1642+
var result =
1643+
_buildSetConstant(set, [element.value], typeSystem, setType);
1644+
return result;
1645+
}
16101646
}
16111647
}
16121648

pkg/analyzer/lib/src/fasta/ast_builder.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4829,21 +4829,24 @@ class AstBuilder extends StackListener {
48294829
assert(optional(':', colon));
48304830
debugEvent("LiteralMapEntry");
48314831

4832-
// TODO(cstefantsova): Handle null-aware map entries.
48334832
if (!enableNullAwareElements &&
48344833
(nullAwareKeyToken != null || nullAwareValueToken != null)) {
48354834
_reportFeatureNotEnabled(
48364835
feature: ExperimentalFeatures.null_aware_elements,
48374836
startToken: nullAwareKeyToken ?? nullAwareValueToken!,
48384837
);
4838+
nullAwareKeyToken = null;
4839+
nullAwareValueToken = null;
48394840
}
48404841

48414842
var value = pop() as ExpressionImpl;
48424843
var key = pop() as ExpressionImpl;
48434844
push(
48444845
MapLiteralEntryImpl(
4846+
keyQuestion: nullAwareKeyToken,
48454847
key: key,
48464848
separator: colon,
4849+
valueQuestion: nullAwareValueToken,
48474850
value: value,
48484851
),
48494852
);
@@ -5139,12 +5142,19 @@ class AstBuilder extends StackListener {
51395142
@override
51405143
void handleNullAwareElement(Token nullAwareElement) {
51415144
debugEvent('NullAwareElement');
5142-
// TODO(cstefantsova): Handle null-aware elements.
51435145
if (!enableNullAwareElements) {
51445146
_reportFeatureNotEnabled(
51455147
feature: ExperimentalFeatures.null_aware_elements,
51465148
startToken: nullAwareElement,
51475149
);
5150+
} else {
5151+
var expression = pop() as ExpressionImpl;
5152+
push(
5153+
NullAwareElementImpl(
5154+
question: nullAwareElement,
5155+
value: expression,
5156+
),
5157+
);
51485158
}
51495159
}
51505160

pkg/analyzer/lib/src/generated/resolver.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3249,6 +3249,21 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
32493249
return imposedType ?? typeProvider.dynamicType;
32503250
}
32513251

3252+
@override
3253+
void visitNullAwareElement(NullAwareElement node,
3254+
{CollectionLiteralContext? context}) {
3255+
inferenceLogWriter?.enterElement(node);
3256+
3257+
var elementType = context?.elementType;
3258+
if (elementType != null) {
3259+
elementType = typeSystem.makeNullable(elementType);
3260+
}
3261+
3262+
analyzeExpression(node.value, elementType ?? UnknownInferredType.instance);
3263+
3264+
inferenceLogWriter?.exitElement(node);
3265+
}
3266+
32523267
@override
32533268
void visitNullLiteral(NullLiteral node,
32543269
{DartType contextType = UnknownInferredType.instance}) {

0 commit comments

Comments
 (0)