parse nested control flow collection entries

Change-Id: Id50c0a53a51f076dd75dd5bc13a694663449bc83
Reviewed-on: https://dart-review.googlesource.com/c/91400
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Dan Rubel 2019-01-29 17:53:58 +00:00
parent 213cfdc85e
commit 09e6a689c2
9 changed files with 466 additions and 54 deletions

View file

@ -1121,6 +1121,11 @@ class HighlightsTestSupport extends AbstractAnalysisTest {
}
void processNotification(Notification notification) {
if (notification.event == SERVER_NOTIFICATION_ERROR) {
print('SERVER_NOTIFICATION_ERROR: ${notification.toJson()}');
_resultsAvailable.complete(null);
fail('SERVER_NOTIFICATION_ERROR');
}
if (notification.event == ANALYSIS_NOTIFICATION_HIGHLIGHTS) {
var params = new AnalysisHighlightsParams.fromNotification(notification);
if (params.file == testFile) {

View file

@ -971,6 +971,11 @@ class HighlightsTestSupport extends AbstractAnalysisTest {
}
void processNotification(Notification notification) {
if (notification.event == SERVER_NOTIFICATION_ERROR) {
print('SERVER_NOTIFICATION_ERROR: ${notification.toJson()}');
_resultsAvailable.complete(null);
fail('SERVER_NOTIFICATION_ERROR');
}
if (notification.event == ANALYSIS_NOTIFICATION_HIGHLIGHTS) {
var params = new AnalysisHighlightsParams.fromNotification(notification);
if (params.file == testFile) {

View file

@ -7987,7 +7987,7 @@ class LocalVariableInfo {
abstract class MapElementImpl extends AstNodeImpl implements MapElement {}
class MapForElementImpl extends CollectionElementImpl
class MapForElementImpl extends MapElementImpl
with ForMixin
implements MapForElement {
/**

View file

@ -3288,28 +3288,28 @@ class _ForControlFlowInfo implements _EntryInfo {
this.forLoopParts, this.rightParenthesis, this.entry);
@override
CollectionElement asCollectionElement(AstFactory ast) {
return ast.collectionForElement(
awaitKeyword: awaitToken,
forKeyword: forKeyword,
leftParenthesis: leftParenthesis,
forLoopParts: forLoopParts,
rightParenthesis: rightParenthesis,
body: entry,
);
}
CollectionElement asCollectionElement(AstFactory ast) =>
ast.collectionForElement(
awaitKeyword: awaitToken,
forKeyword: forKeyword,
leftParenthesis: leftParenthesis,
forLoopParts: forLoopParts,
rightParenthesis: rightParenthesis,
body: entry is _EntryInfo
? entry.asCollectionElement(ast)
: entry as CollectionElement,
);
@override
MapElement asMapElement(AstFactory ast) {
return ast.mapForElement(
awaitKeyword: awaitToken,
forKeyword: forKeyword,
leftParenthesis: leftParenthesis,
forLoopParts: forLoopParts,
rightParenthesis: rightParenthesis,
body: entry,
);
}
MapElement asMapElement(AstFactory ast) => ast.mapForElement(
awaitKeyword: awaitToken,
forKeyword: forKeyword,
leftParenthesis: leftParenthesis,
forLoopParts: forLoopParts,
rightParenthesis: rightParenthesis,
body:
entry is _EntryInfo ? entry.asMapElement(ast) : entry as MapElement,
);
}
class _IfControlFlowInfo implements _EntryInfo {

View file

@ -148,6 +148,34 @@ class CollectionLiteralParserTest extends FastaParserTestCase {
expect(iterable.name, 'list');
}
void test_listLiteral_forIf() {
ListLiteral2 list = parseCollectionLiteral(
'[1, await for (var x in list) if (c) 2]',
inAsync: true,
);
expect(list.elements, hasLength(2));
IntegerLiteral first = list.elements[0];
expect(first.value, 1);
CollectionForElement second = list.elements[1];
expect(second.awaitKeyword, isNotNull);
expect(second.forKeyword.isKeyword, isTrue);
expect(second.leftParenthesis.lexeme, '(');
expect(second.rightParenthesis.lexeme, ')');
ForEachPartsWithDeclaration forLoopParts = second.forLoopParts;
DeclaredIdentifier forLoopVar = forLoopParts.loopVariable;
expect(forLoopVar.identifier.name, 'x');
expect(forLoopParts.inKeyword, isNotNull);
SimpleIdentifier iterable = forLoopParts.iterable;
expect(iterable.name, 'list');
CollectionIfElement body = second.body;
SimpleIdentifier condition = body.condition;
expect(condition.name, 'c');
IntegerLiteral thenElement = body.thenElement;
expect(thenElement.value, 2);
}
void test_listLiteral_forSpread() {
ListLiteral2 list =
parseCollectionLiteral('[1, for (int x = 0; x < 10; ++x) ...[2]]');
@ -200,6 +228,46 @@ class CollectionLiteralParserTest extends FastaParserTestCase {
expect(elseElement.value, 5);
}
void test_listLiteral_ifElseFor() {
ListLiteral2 list =
parseCollectionLiteral('[1, if (true) 2 else for (a in b) 5]');
expect(list.elements, hasLength(2));
IntegerLiteral first = list.elements[0];
expect(first.value, 1);
CollectionIfElement second = list.elements[1];
BooleanLiteral condition = second.condition;
expect(condition.value, isTrue);
IntegerLiteral thenElement = second.thenElement;
expect(thenElement.value, 2);
CollectionForElement elseElement = second.elseElement;
ForEachPartsWithIdentifier forLoopParts = elseElement.forLoopParts;
expect(forLoopParts.identifier.name, 'a');
IntegerLiteral forValue = elseElement.body;
expect(forValue.value, 5);
}
void test_listLiteral_ifFor() {
ListLiteral2 list = parseCollectionLiteral('[1, if (true) for (a in b) 2]');
expect(list.elements, hasLength(2));
IntegerLiteral first = list.elements[0];
expect(first.value, 1);
CollectionIfElement second = list.elements[1];
BooleanLiteral condition = second.condition;
expect(condition.value, isTrue);
CollectionForElement thenElement = second.thenElement;
ForEachPartsWithIdentifier forLoopParts = thenElement.forLoopParts;
expect(forLoopParts.identifier.name, 'a');
IntegerLiteral forValue = thenElement.body;
expect(forValue.value, 2);
expect(second.elseElement, isNull);
}
void test_listLiteral_ifSpread() {
ListLiteral2 list = parseCollectionLiteral('[1, if (true) ...[2]]');
expect(list.elements, hasLength(2));
@ -275,6 +343,35 @@ class CollectionLiteralParserTest extends FastaParserTestCase {
expect(iterable.name, 'list');
}
void test_mapLiteral_forIf() {
MapLiteral2 map = parseCollectionLiteral(
'{1:7, await for (y in list) if (c) 2:3}',
inAsync: true);
expect(map.entries, hasLength(2));
MapLiteralEntry first = map.entries[0];
IntegerLiteral firstValue = first.value;
expect(firstValue.value, 7);
MapForElement second = map.entries[1];
expect(second.awaitKeyword, isNotNull);
expect(second.forKeyword.isKeyword, isTrue);
expect(second.leftParenthesis.lexeme, '(');
expect(second.rightParenthesis.lexeme, ')');
ForEachPartsWithIdentifier forLoopParts = second.forLoopParts;
SimpleIdentifier forLoopVar = forLoopParts.identifier;
expect(forLoopVar.name, 'y');
expect(forLoopParts.inKeyword, isNotNull);
SimpleIdentifier iterable = forLoopParts.iterable;
expect(iterable.name, 'list');
MapIfElement body = second.body;
SimpleIdentifier condition = body.condition;
expect(condition.name, 'c');
MapLiteralEntry thenElement = body.thenElement;
IntegerLiteral thenValue = thenElement.value;
expect(thenValue.value, 3);
}
void test_mapLiteral_forSpread() {
MapLiteral2 map =
parseCollectionLiteral('{1:7, for (x = 0; x < 10; ++x) ...{2:3}}');
@ -334,6 +431,52 @@ class CollectionLiteralParserTest extends FastaParserTestCase {
expect(elseElementValue.value, 6);
}
void test_mapLiteral_ifElseFor() {
MapLiteral2 map =
parseCollectionLiteral('{1:1, if (true) 2:4 else for (c in d) 5:6}');
expect(map.entries, hasLength(2));
MapLiteralEntry first = map.entries[0];
IntegerLiteral firstValue = first.value;
expect(firstValue.value, 1);
MapIfElement second = map.entries[1];
BooleanLiteral condition = second.condition;
expect(condition.value, isTrue);
MapLiteralEntry thenElement = second.thenElement;
IntegerLiteral thenElementValue = thenElement.value;
expect(thenElementValue.value, 4);
MapForElement elseElement = second.elseElement;
ForEachPartsWithIdentifier forLoopParts = elseElement.forLoopParts;
expect(forLoopParts.identifier.name, 'c');
MapLiteralEntry body = elseElement.body;
IntegerLiteral bodyValue = body.value;
expect(bodyValue.value, 6);
}
void test_mapLiteral_ifFor() {
MapLiteral2 map =
parseCollectionLiteral('{1:1, if (true) for (a in b) 2:4}');
expect(map.entries, hasLength(2));
MapLiteralEntry first = map.entries[0];
IntegerLiteral firstValue = first.value;
expect(firstValue.value, 1);
MapIfElement second = map.entries[1];
BooleanLiteral condition = second.condition;
expect(condition.value, isTrue);
MapForElement thenElement = second.thenElement;
ForEachPartsWithIdentifier forLoopParts = thenElement.forLoopParts;
expect(forLoopParts.identifier.name, 'a');
MapLiteralEntry body = thenElement.body;
IntegerLiteral thenElementValue = body.value;
expect(thenElementValue.value, 4);
expect(second.elseElement, isNull);
}
void test_mapLiteral_ifSpread() {
MapLiteral2 map = parseCollectionLiteral('{1:1, if (true) ...{2:4}}');
expect(map.entries, hasLength(2));

View file

@ -925,9 +925,7 @@ class ConstantVisitorWithFlowControlAndSpreadCollectionsTest
bool get enableNewAnalysisDriver => true;
@failingTest
test_listLiteral_nested() async {
// Fails because we're not yet parsing nested elements.
CompilationUnit compilationUnit = await resolveSource('''
const c = [1, if (1 > 0) if (2 > 1) 2, 3];
''');
@ -1082,9 +1080,7 @@ const c = {'a' : 1, ...{'b' : 2, 'c' : 3}, 'd' : 4};
value.values.map((e) => e.toIntValue()), unorderedEquals([1, 2, 3, 4]));
}
@failingTest
test_setLiteral_nested() async {
// Fails because we're not yet parsing nested elements.
CompilationUnit compilationUnit = await resolveSource('''
const c = {1, if (1 > 0) if (2 > 1) 2, 3};
''');

View file

@ -49,9 +49,8 @@ LiteralEntryInfo computeLiteralEntry(Token token) {
Token next = token.next;
if (optional('if', next)) {
return ifCondition;
} else if (optional('for', next)) {
return new ForCondition();
} else if (optional('await', next) && optional('for', next.next)) {
} else if (optional('for', next) ||
(optional('await', next) && optional('for', next.next))) {
return new ForCondition();
} else if (optional('...', next) || optional('...?', next)) {
return spreadOperator;

View file

@ -57,10 +57,20 @@ class ForCondition extends LiteralEntryInfo {
@override
LiteralEntryInfo computeNext(Token token) {
Token next = token.next;
if (optional('...', next) || optional('...?', next)) {
if (optional('for', next) ||
(optional('await', next) && optional('for', next.next))) {
return new Nested(
new ForCondition(),
inStyle ? const ForInComplete() : const ForComplete(),
);
} else if (optional('if', next)) {
return new Nested(
ifCondition,
inStyle ? const ForInComplete() : const ForComplete(),
);
} else if (optional('...', next) || optional('...?', next)) {
return inStyle ? const ForInSpread() : const ForSpread();
}
// TODO(danrubel): nested control flow structures
return inStyle ? const ForInEntry() : const ForEntry();
}
}
@ -144,10 +154,14 @@ class IfCondition extends LiteralEntryInfo {
@override
LiteralEntryInfo computeNext(Token token) {
Token next = token.next;
if (optional('...', next) || optional('...?', next)) {
if (optional('for', next) ||
(optional('await', next) && optional('for', next.next))) {
return new Nested(new ForCondition(), const IfComplete());
} else if (optional('if', next)) {
return new Nested(ifCondition, const IfComplete());
} else if (optional('...', next) || optional('...?', next)) {
return const IfSpread();
}
// TODO(danrubel): nested control flow structures
return const IfEntry();
}
}
@ -158,12 +172,7 @@ class IfSpread extends SpreadOperator {
const IfSpread();
@override
LiteralEntryInfo computeNext(Token token) {
if (optional('else', token.next)) {
return const IfElse();
}
return const IfComplete();
}
LiteralEntryInfo computeNext(Token token) => const IfComplete();
}
/// A step for parsing a literal list, set, or map entry
@ -172,11 +181,23 @@ class IfEntry extends LiteralEntryInfo {
const IfEntry() : super(true);
@override
LiteralEntryInfo computeNext(Token token) {
if (optional('else', token.next)) {
return const IfElse();
LiteralEntryInfo computeNext(Token token) => const IfComplete();
}
class IfComplete extends LiteralEntryInfo {
const IfComplete() : super(false);
@override
Token parse(Token token, Parser parser) {
if (!optional('else', token.next)) {
parser.listener.endIfControlFlow(token);
}
return const IfComplete();
return token;
}
@override
LiteralEntryInfo computeNext(Token token) {
return optional('else', token.next) ? const IfElse() : null;
}
}
@ -196,10 +217,14 @@ class IfElse extends LiteralEntryInfo {
LiteralEntryInfo computeNext(Token token) {
assert(optional('else', token));
Token next = token.next;
if (optional('...', next) || optional('...?', next)) {
if (optional('for', next) ||
(optional('await', next) && optional('for', next.next))) {
return new Nested(new ForCondition(), const IfElseComplete());
} else if (optional('if', next)) {
return new Nested(ifCondition, const IfElseComplete());
} else if (optional('...', next) || optional('...?', next)) {
return const ElseSpread();
}
// TODO(danrubel): nested control flow structures
return const ElseEntry();
}
}
@ -222,16 +247,6 @@ class ElseEntry extends LiteralEntryInfo {
}
}
class IfComplete extends LiteralEntryInfo {
const IfComplete() : super(false);
@override
Token parse(Token token, Parser parser) {
parser.listener.endIfControlFlow(token);
return token;
}
}
class IfElseComplete extends LiteralEntryInfo {
const IfElseComplete() : super(false);
@ -255,3 +270,22 @@ class SpreadOperator extends LiteralEntryInfo {
return token;
}
}
class Nested extends LiteralEntryInfo {
LiteralEntryInfo nestedStep;
final LiteralEntryInfo lastStep;
Nested(this.nestedStep, this.lastStep) : super(false);
@override
bool get hasEntry => nestedStep.hasEntry;
@override
Token parse(Token token, Parser parser) => nestedStep.parse(token, parser);
@override
LiteralEntryInfo computeNext(Token token) {
nestedStep = nestedStep.computeNext(token);
return nestedStep != null ? this : lastStep;
}
}

View file

@ -96,6 +96,143 @@ class CollectionElementTest {
);
}
test_forForFor() {
parseEntry(
'before for (var i = 0; i < 10; ++i) for (x in y) for (var a in [6]) 2',
[
// for (var i = 0; i < 10; ++i)
'beginForControlFlow null for',
'beginMetadataStar var',
'endMetadataStar 0',
'handleNoTypeArguments var',
'beginVariablesDeclaration i var',
'handleIdentifier i localVariableDeclaration',
'beginInitializedIdentifier i',
'beginVariableInitializer =',
'handleLiteralInt 0',
'endVariableInitializer =',
'endInitializedIdentifier i',
'endVariablesDeclaration 1 null',
'handleForInitializerLocalVariableDeclaration 0',
'handleIdentifier i expression',
'handleNoTypeArguments <',
'handleNoArguments <',
'handleSend i <',
'beginBinaryExpression <',
'handleLiteralInt 10',
'endBinaryExpression <',
'handleExpressionStatement ;',
'handleIdentifier i expression',
'handleNoTypeArguments )',
'handleNoArguments )',
'handleSend i )',
'handleUnaryPrefixAssignmentExpression ++',
'handleForInitializerExpressionStatement for ( ; 1',
// nested for (x in y)
'beginForControlFlow null for',
'handleIdentifier x expression',
'handleNoTypeArguments in',
'handleNoArguments in',
'handleSend x in',
'handleForInitializerExpressionStatement x',
'beginForInExpression y',
'handleIdentifier y expression',
'handleNoTypeArguments )',
'handleNoArguments )',
'handleSend y )',
'endForInExpression )',
'handleForInLoopParts null for ( in',
// nested for (var a in [6])
'beginForControlFlow null for',
'beginMetadataStar var',
'endMetadataStar 0',
'handleNoTypeArguments var',
'beginVariablesDeclaration a var',
'handleIdentifier a localVariableDeclaration',
'beginInitializedIdentifier a',
'handleNoVariableInitializer in',
'endInitializedIdentifier a',
'endVariablesDeclaration 1 null',
'handleForInitializerLocalVariableDeclaration a',
'beginForInExpression [',
'handleNoTypeArguments [',
'handleLiteralInt 6',
'handleLiteralList 1, [, null, ]',
'endForInExpression )',
'handleForInLoopParts null for ( in',
// entry
'handleLiteralInt 2',
// end nested for
'endForInControlFlow 2',
// end nested for
'endForInControlFlow 2',
// end for
'endForControlFlow 2',
],
);
}
test_forIfForElse() {
parseEntry(
'before await for (var x in y) if (a) for (b in c) 2 else 7',
[
// await for (var x in y)
'beginForControlFlow await for',
'beginMetadataStar var',
'endMetadataStar 0',
'handleNoTypeArguments var',
'beginVariablesDeclaration x var',
'handleIdentifier x localVariableDeclaration',
'beginInitializedIdentifier x',
'handleNoVariableInitializer in',
'endInitializedIdentifier x',
'endVariablesDeclaration 1 null',
'handleForInitializerLocalVariableDeclaration x',
'beginForInExpression y',
'handleIdentifier y expression',
'handleNoTypeArguments )',
'handleNoArguments )',
'handleSend y )',
'endForInExpression )',
'handleForInLoopParts await for ( in',
// nested if (a)
'beginIfControlFlow if',
'handleIdentifier a expression',
'handleNoTypeArguments )',
'handleNoArguments )',
'handleSend a )',
'handleParenthesizedCondition (',
// nested for (b in c)
'beginForControlFlow null for',
'handleIdentifier b expression',
'handleNoTypeArguments in',
'handleNoArguments in',
'handleSend b in',
'handleForInitializerExpressionStatement b',
'beginForInExpression c',
'handleIdentifier c expression',
'handleNoTypeArguments )',
'handleNoArguments )',
'handleSend c )',
'endForInExpression )',
'handleForInLoopParts null for ( in',
// for-if-for-entry
'handleLiteralInt 2',
// end nested for
'endForInControlFlow 2',
// nested else
'handleElseControlFlow else',
// nested if-else-entry
'handleLiteralInt 7',
// end nested else
'endIfElseControlFlow 7',
// end for
'endForInControlFlow 7',
],
inAsync: true,
);
}
test_forIn() {
parseEntry(
'before await for (var x in y) 2',
@ -219,6 +356,99 @@ class CollectionElementTest {
);
}
test_ifFor() {
parseEntry(
'before if (true) for (x in y) 2',
[
// if (true)
'beginIfControlFlow if',
'handleLiteralBool true',
'handleParenthesizedCondition (',
// nested for (x in y)
'beginForControlFlow null for',
'handleIdentifier x expression',
'handleNoTypeArguments in',
'handleNoArguments in',
'handleSend x in',
'handleForInitializerExpressionStatement x',
'beginForInExpression y',
'handleIdentifier y expression',
'handleNoTypeArguments )',
'handleNoArguments )',
'handleSend y )',
'endForInExpression )',
'handleForInLoopParts null for ( in',
// if-for-entry
'handleLiteralInt 2',
// end nested for
'endForInControlFlow 2',
// end if
'endIfControlFlow 2',
],
);
}
test_ifForElseIfFor() {
parseEntry(
'before if (true) for (a in b) 2 else if (c) for (d in e) 5',
[
// if (true)
'beginIfControlFlow if',
'handleLiteralBool true',
'handleParenthesizedCondition (',
// nested for (a in b)
'beginForControlFlow null for',
'handleIdentifier a expression',
'handleNoTypeArguments in',
'handleNoArguments in',
'handleSend a in',
'handleForInitializerExpressionStatement a',
'beginForInExpression b',
'handleIdentifier b expression',
'handleNoTypeArguments )',
'handleNoArguments )',
'handleSend b )',
'endForInExpression )',
'handleForInLoopParts null for ( in',
// if-for-entry
'handleLiteralInt 2',
// end nested for
'endForInControlFlow 2',
// else
'handleElseControlFlow else',
// nested if (c)
'beginIfControlFlow if',
'handleIdentifier c expression',
'handleNoTypeArguments )',
'handleNoArguments )',
'handleSend c )',
'handleParenthesizedCondition (',
// nested for (d in e)
'beginForControlFlow null for',
'handleIdentifier d expression',
'handleNoTypeArguments in',
'handleNoArguments in',
'handleSend d in',
'handleForInitializerExpressionStatement d',
'beginForInExpression e',
'handleIdentifier e expression',
'handleNoTypeArguments )',
'handleNoArguments )',
'handleSend e )',
'endForInExpression )',
'handleForInLoopParts null for ( in',
// else-if-for-entry
'handleLiteralInt 5',
// end nested for
'endForInControlFlow 5',
// end nested if
'endIfControlFlow 5',
// end else
'endIfElseControlFlow 5',
],
);
}
test_ifSpreadQ() {
parseEntry(
'before if (true) ...?[2]',