Add summary support for `if` elements.
Change-Id: I32582b9608bcf92cabfac94e1d796036198818a5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97023
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
index 2cd3ae4..a2e6994 100644
--- a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
+++ b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
@@ -670,6 +670,20 @@
.toList();
}
+ static IfElement ifElement(
+ Expression condition, CollectionElement thenElement,
+ [CollectionElement elseElement]) =>
+ astFactory.ifElement(
+ ifKeyword: TokenFactory.tokenFromKeyword(Keyword.IF),
+ leftParenthesis: TokenFactory.tokenFromType(TokenType.OPEN_PAREN),
+ condition: condition,
+ rightParenthesis: TokenFactory.tokenFromType(TokenType.CLOSE_PAREN),
+ thenElement: thenElement,
+ elseKeyword: elseElement == null
+ ? null
+ : TokenFactory.tokenFromKeyword(Keyword.ELSE),
+ elseElement: elseElement);
+
static IfStatement ifStatement(
Expression condition, Statement thenStatement) =>
ifStatement2(condition, thenStatement, null);
diff --git a/pkg/analyzer/lib/src/summary/expr_builder.dart b/pkg/analyzer/lib/src/summary/expr_builder.dart
index 629798e..960e342 100644
--- a/pkg/analyzer/lib/src/summary/expr_builder.dart
+++ b/pkg/analyzer/lib/src/summary/expr_builder.dart
@@ -323,6 +323,12 @@
case UnlinkedExprOperation.nullAwareSpreadElement:
_pushSpread(TokenType.PERIOD_PERIOD_PERIOD_QUESTION);
break;
+ case UnlinkedExprOperation.ifElement:
+ _pushIfElement(false);
+ break;
+ case UnlinkedExprOperation.ifElseElement:
+ _pushIfElement(true);
+ break;
case UnlinkedExprOperation.cascadeSectionBegin:
case UnlinkedExprOperation.cascadeSectionEnd:
case UnlinkedExprOperation.pushLocalFunctionReference:
@@ -650,6 +656,13 @@
_push(AstTestFactory.propertyAccess(target, propertyNode));
}
+ void _pushIfElement(bool hasElse) {
+ CollectionElement elseElement = hasElse ? _popCollectionElement() : null;
+ CollectionElement thenElement = _popCollectionElement();
+ Expression condition = _pop();
+ _push(AstTestFactory.ifElement(condition, thenElement, elseElement));
+ }
+
void _pushInstanceCreation() {
EntityRef ref = _uc.references[refPtr++];
ReferenceInfo info = resynthesizer.getReferenceInfo(ref.reference);
diff --git a/pkg/analyzer/lib/src/summary/format.fbs b/pkg/analyzer/lib/src/summary/format.fbs
index 6745d48..6cd172f 100644
--- a/pkg/analyzer/lib/src/summary/format.fbs
+++ b/pkg/analyzer/lib/src/summary/format.fbs
@@ -980,7 +980,18 @@
/// Pop the top value from the stack, convert it to a spread element of type
/// `...?`, and push the result back onto the stack.
- nullAwareSpreadElement
+ nullAwareSpreadElement,
+
+ /// Pop the top two values from the stack. The first is a condition
+ /// and the second is a collection element. Push an "if" element having the
+ /// given condition, with the collection element as its "then" clause.
+ ifElement,
+
+ /// Pop the top three values from the stack. The first is a condition and the
+ /// other two are collection elements. Push an "if" element having the given
+ /// condition, with the two collection elements as its "then" and "else"
+ /// clauses, respectively.
+ ifElseElement
}
/// Enum used to indicate the kind of a parameter.
diff --git a/pkg/analyzer/lib/src/summary/idl.dart b/pkg/analyzer/lib/src/summary/idl.dart
index f000abb..c9e84f8 100644
--- a/pkg/analyzer/lib/src/summary/idl.dart
+++ b/pkg/analyzer/lib/src/summary/idl.dart
@@ -3876,6 +3876,17 @@
/// Pop the top value from the stack, convert it to a spread element of type
/// `...?`, and push the result back onto the stack.
nullAwareSpreadElement,
+
+ /// Pop the top two values from the stack. The first is a condition
+ /// and the second is a collection element. Push an "if" element having the
+ /// given condition, with the collection element as its "then" clause.
+ ifElement,
+
+ /// Pop the top three values from the stack. The first is a condition and the
+ /// other two are collection elements. Push an "if" element having the given
+ /// condition, with the two collection elements as its "then" and "else"
+ /// clauses, respectively.
+ ifElseElement,
}
/// Unlinked summary information about an import declaration.
diff --git a/pkg/analyzer/lib/src/summary/summarize_const_expr.dart b/pkg/analyzer/lib/src/summary/summarize_const_expr.dart
index 71af96b..f367937 100644
--- a/pkg/analyzer/lib/src/summary/summarize_const_expr.dart
+++ b/pkg/analyzer/lib/src/summary/summarize_const_expr.dart
@@ -548,6 +548,16 @@
operations.add(isNullAware
? UnlinkedExprOperation.nullAwareSpreadElement
: UnlinkedExprOperation.spreadElement);
+ } else if (element is IfElement) {
+ _serialize(element.condition);
+ _serializeCollectionElement(element.thenElement);
+ var elseElement = element.elseElement;
+ if (elseElement == null) {
+ operations.add(UnlinkedExprOperation.ifElement);
+ } else {
+ _serializeCollectionElement(elseElement);
+ operations.add(UnlinkedExprOperation.ifElseElement);
+ }
} else {
// TODO(paulberry): Implement serialization for spread and control flow
// elements.
diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart
index 6813ec5..b3a61b2 100644
--- a/pkg/analyzer/test/src/summary/element_text.dart
+++ b/pkg/analyzer/test/src/summary/element_text.dart
@@ -673,6 +673,16 @@
} else if (e is SpreadElement) {
buffer.write(e.spreadOperator.lexeme);
writeNode(e.expression);
+ } else if (e is IfElement) {
+ buffer.write('if (');
+ writeNode(e.condition);
+ buffer.write(') ');
+ writeNode(e.thenElement);
+ var elseElement = e.elseElement;
+ if (elseElement != null) {
+ buffer.write(' else ');
+ writeNode(elseElement);
+ }
} else {
fail('Unsupported expression type: ${e.runtimeType}');
}
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index d90e62a..bb73374 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -2556,6 +2556,34 @@
''');
}
+ test_const_list_if() async {
+ experimentStatus = ExperimentStatus(control_flow_collections: true);
+ var library = await checkLibrary('''
+const Object x = const <int>[if (true) 1];
+''');
+ checkElementText(
+ library,
+ '''
+const Object x = const <
+ int/*location: dart:core;int*/>[if (true) 1];
+''',
+ withTypes: true);
+ }
+
+ test_const_list_if_else() async {
+ experimentStatus = ExperimentStatus(control_flow_collections: true);
+ var library = await checkLibrary('''
+const Object x = const <int>[if (true) 1 else 2];
+''');
+ checkElementText(
+ library,
+ '''
+const Object x = const <
+ int/*location: dart:core;int*/>[if (true) 1 else 2];
+''',
+ withTypes: true);
+ }
+
test_const_list_inferredType() async {
// The summary needs to contain enough information so that when the constant
// is resynthesized, the constant value can get the type that was computed
@@ -2624,6 +2652,36 @@
}
}
+ test_const_map_if() async {
+ experimentStatus = ExperimentStatus(control_flow_collections: true);
+ var library = await checkLibrary('''
+const Object x = const <int, int>{if (true) 1: 2};
+''');
+ checkElementText(
+ library,
+ '''
+const Object x = const <
+ int/*location: dart:core;int*/,
+ int/*location: dart:core;int*/>{if (true) 1: 2}/*isMap*/;
+''',
+ withTypes: true);
+ }
+
+ test_const_map_if_else() async {
+ experimentStatus = ExperimentStatus(control_flow_collections: true);
+ var library = await checkLibrary('''
+const Object x = const <int, int>{if (true) 1: 2 else 3: 4];
+''');
+ checkElementText(
+ library,
+ '''
+const Object x = const <
+ int/*location: dart:core;int*/,
+ int/*location: dart:core;int*/>{if (true) 1: 2 else 3: 4}/*isMap*/;
+''',
+ withTypes: true);
+ }
+
test_const_map_inferredType() async {
// The summary needs to contain enough information so that when the constant
// is resynthesized, the constant value can get the type that was computed
@@ -3141,6 +3199,34 @@
''');
}
+ test_const_set_if() async {
+ experimentStatus = ExperimentStatus(control_flow_collections: true);
+ var library = await checkLibrary('''
+const Object x = const <int>{if (true) 1};
+''');
+ checkElementText(
+ library,
+ '''
+const Object x = const <
+ int/*location: dart:core;int*/>{if (true) 1}/*isSet*/;
+''',
+ withTypes: true);
+ }
+
+ test_const_set_if_else() async {
+ experimentStatus = ExperimentStatus(control_flow_collections: true);
+ var library = await checkLibrary('''
+const Object x = const <int>{if (true) 1 else 2];
+''');
+ checkElementText(
+ library,
+ '''
+const Object x = const <
+ int/*location: dart:core;int*/>{if (true) 1 else 2}/*isSet*/;
+''',
+ withTypes: true);
+ }
+
test_const_set_inferredType() async {
// The summary needs to contain enough information so that when the constant
// is resynthesized, the constant value can get the type that was computed
diff --git a/pkg/analyzer/test/src/summary/summary_common.dart b/pkg/analyzer/test/src/summary/summary_common.dart
index c69533d..e5a4dfb 100644
--- a/pkg/analyzer/test/src/summary/summary_common.dart
+++ b/pkg/analyzer/test/src/summary/summary_common.dart
@@ -2754,6 +2754,44 @@
]);
}
+ test_constExpr_list_if() {
+ experimentStatus = ExperimentStatus(
+ control_flow_collections: true, spread_collections: true);
+ UnlinkedVariable variable =
+ serializeVariableText('const v = [if (true) 1];');
+ assertUnlinkedConst(variable.initializer.bodyExpr, '[if (true) 1]',
+ operators: [
+ UnlinkedExprOperation.pushTrue,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.ifElement,
+ UnlinkedExprOperation.makeUntypedList
+ ],
+ ints: [
+ 1,
+ 1
+ ]);
+ }
+
+ test_constExpr_list_if_else() {
+ experimentStatus = ExperimentStatus(
+ control_flow_collections: true, spread_collections: true);
+ UnlinkedVariable variable =
+ serializeVariableText('const v = [if (true) 1 else 2];');
+ assertUnlinkedConst(variable.initializer.bodyExpr, '[if (true) 1 else 2]',
+ operators: [
+ UnlinkedExprOperation.pushTrue,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.ifElseElement,
+ UnlinkedExprOperation.makeUntypedList
+ ],
+ ints: [
+ 1,
+ 2,
+ 1
+ ]);
+ }
+
test_constExpr_list_spread() {
experimentStatus = ExperimentStatus(
control_flow_collections: true, spread_collections: true);
@@ -3151,6 +3189,67 @@
);
}
+ test_constExpr_map_if() {
+ experimentStatus = ExperimentStatus(
+ control_flow_collections: true, spread_collections: true);
+ UnlinkedVariable variable =
+ serializeVariableText('const v = <int, int>{if (true) 1 : 2};');
+ assertUnlinkedConst(
+ variable.initializer.bodyExpr, '<int, int>{if (true) 1 : 2}',
+ operators: [
+ UnlinkedExprOperation.pushTrue,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.makeMapLiteralEntry,
+ UnlinkedExprOperation.ifElement,
+ UnlinkedExprOperation.makeTypedMap2
+ ],
+ ints: [
+ 1,
+ 2,
+ 1
+ ],
+ referenceValidators: [
+ (EntityRef r) => checkTypeRef(r, 'dart:core', 'int',
+ expectedKind: ReferenceKind.classOrEnum),
+ (EntityRef r) => checkTypeRef(r, 'dart:core', 'int',
+ expectedKind: ReferenceKind.classOrEnum)
+ ]);
+ }
+
+ test_constExpr_map_if_else() {
+ experimentStatus = ExperimentStatus(
+ control_flow_collections: true, spread_collections: true);
+ UnlinkedVariable variable = serializeVariableText(
+ 'const v = <int, int>{if (true) 1 : 2 else 3 : 4};');
+ assertUnlinkedConst(
+ variable.initializer.bodyExpr, '<int, int>{if (true) 1 : 2 else 3 : 4}',
+ operators: [
+ UnlinkedExprOperation.pushTrue,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.makeMapLiteralEntry,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.makeMapLiteralEntry,
+ UnlinkedExprOperation.ifElseElement,
+ UnlinkedExprOperation.makeTypedMap2
+ ],
+ ints: [
+ 1,
+ 2,
+ 3,
+ 4,
+ 1
+ ],
+ referenceValidators: [
+ (EntityRef r) => checkTypeRef(r, 'dart:core', 'int',
+ expectedKind: ReferenceKind.classOrEnum),
+ (EntityRef r) => checkTypeRef(r, 'dart:core', 'int',
+ expectedKind: ReferenceKind.classOrEnum)
+ ]);
+ }
+
test_constExpr_map_spread() {
experimentStatus = ExperimentStatus(
control_flow_collections: true, spread_collections: true);
@@ -3925,6 +4024,53 @@
operators: [UnlinkedExprOperation.pushTrue]);
}
+ test_constExpr_set_if() {
+ experimentStatus = ExperimentStatus(
+ control_flow_collections: true, spread_collections: true);
+ UnlinkedVariable variable =
+ serializeVariableText('const v = <int>{if (true) 1};');
+ assertUnlinkedConst(variable.initializer.bodyExpr, '<int>{if (true) 1}',
+ operators: [
+ UnlinkedExprOperation.pushTrue,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.ifElement,
+ UnlinkedExprOperation.makeTypedSet
+ ],
+ ints: [
+ 1,
+ 1
+ ],
+ referenceValidators: [
+ (EntityRef r) => checkTypeRef(r, 'dart:core', 'int',
+ expectedKind: ReferenceKind.classOrEnum)
+ ]);
+ }
+
+ test_constExpr_set_if_else() {
+ experimentStatus = ExperimentStatus(
+ control_flow_collections: true, spread_collections: true);
+ UnlinkedVariable variable =
+ serializeVariableText('const v = <int>{if (true) 1 else 2};');
+ assertUnlinkedConst(
+ variable.initializer.bodyExpr, '<int>{if (true) 1 else 2}',
+ operators: [
+ UnlinkedExprOperation.pushTrue,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.pushInt,
+ UnlinkedExprOperation.ifElseElement,
+ UnlinkedExprOperation.makeTypedSet
+ ],
+ ints: [
+ 1,
+ 2,
+ 1
+ ],
+ referenceValidators: [
+ (EntityRef r) => checkTypeRef(r, 'dart:core', 'int',
+ expectedKind: ReferenceKind.classOrEnum)
+ ]);
+ }
+
test_constExpr_set_spread() {
experimentStatus = ExperimentStatus(
control_flow_collections: true, spread_collections: true);