Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parser to continue parsing key-value pairs after an If-Statement value in a HashExpression #7002

Merged
merged 4 commits into from Jun 6, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/System.Management.Automation/engine/parser/Parser.cs
Expand Up @@ -2411,6 +2411,16 @@ private StatementAst IfStatementRule(Token ifToken)

clauses.Add(new IfClause(condition, body));

// Save a restore point here. In case there is no 'elseif' or 'else' following,
// we should resync back here to preserve the possible new lines. The new lines
// could be important for the following parsing. For example, in case we are in
// a HashExpression, a new line might be needed for parsing the key-value that
// is following the if statement:
// @{
// a = if (1) {}
// b = 10
// }
var restorePoint = PeekToken();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use PeekToken() instead of _tokenizer.GetRestorePoint() here because it's possible there is an unget token (_ungetToken != null) at this point, and in that case, _tokenizer.GetRestorePoint() will return the offset after the unget token, and we will lose the unget token when resyncing.

Copy link
Member Author

@daxian-dbw daxian-dbw Jun 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decide to directly check _ungotToken == null. I think this way will deliver the intent more clearly. And also, in more common cases, _ungotToken would be null when reaching here.

SkipNewlines();
keyword = PeekToken();

Expand All @@ -2419,8 +2429,7 @@ private StatementAst IfStatementRule(Token ifToken)
SkipToken();
continue;
}

if (keyword.Kind == TokenKind.Else)
else if (keyword.Kind == TokenKind.Else)
{
SkipToken();
SkipNewlines();
Expand All @@ -2436,6 +2445,11 @@ private StatementAst IfStatementRule(Token ifToken)
return new ErrorStatementAst(ExtentOf(ifToken, keyword), componentAsts);
}
}
else
{
// There is no 'elseif' or 'else' following, so resync back to the possible new lines.
Resync(restorePoint);
}
break;
}

Expand Down
Expand Up @@ -212,3 +212,22 @@ Describe "Members of System.Type" -Tags "CI" {
[MyType].ImplementedInterfaces | Should -Be System.Collections.IEnumerable
}
}

Describe "Hash expression with if statement as value" -Tags "CI" {
It "Key-value pairs after an if-statement-value in a HashExpression should continue to be parsed" {
$result = @{
a = if (1) {'a'}
b = 'b'
c = if (0) {2} elseif (1) {'c'}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also test:

  • newlines after }
  • if/elseif/else

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Thanks!

d = 'd'
e = if (0) {2} else {'e'}
f = 'f'
}
$result['a'] | Should -BeExactly 'a'
$result['b'] | Should -BeExactly 'b'
$result['c'] | Should -BeExactly 'c'
$result['d'] | Should -BeExactly 'd'
$result['e'] | Should -BeExactly 'e'
$result['f'] | Should -BeExactly 'f'
}
}