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

OnlyCheckWhitespaceInsideParenthesis Fixes #5863 #5868

Merged
merged 8 commits into from
Nov 11, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
22 changes: 17 additions & 5 deletions src/Build.UnitTests/Scanner_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Build.Evaluation;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Utilities;
using Shouldly;
using Xunit;


Expand Down Expand Up @@ -104,16 +105,27 @@ public void IllFormedProperty()
/// <summary>
/// Tests the space errors case
/// </summary>
[Fact]
public void SpaceProperty()
[Theory]
[InlineData("$(x )")]
[InlineData("$( x)")]
public void SpaceProperty(string pattern)
{
Scanner lexer = new Scanner("$(x )", ParserOptions.AllowProperties);
Scanner lexer = new Scanner(pattern, ParserOptions.AllowProperties);
AdvanceToScannerError(lexer);
Assert.Equal("IllFormedPropertySpaceInCondition", lexer.GetErrorResource());
}

lexer = new Scanner("$( x)", ParserOptions.AllowProperties);
/// <summary>
/// Tests the space not next to end so no errors case
/// </summary>
[Theory]
[InlineData("$(x x)")]
[InlineData("$(y d f ( hi ) sx)")]
Copy link
Member

Choose a reason for hiding this comment

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

These are examples of things we wish we could catch but we can't with current limitations. Can you instead use examples we want to work like

Suggested change
[InlineData("$(x x)")]
[InlineData("$(y d f ( hi ) sx)")]
[InlineData("$(x.StartsWith( 'y' ))")]
[InlineData("$(x.StartsWith ('y'))")]

?

Copy link
Member

Choose a reason for hiding this comment

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

Actually, looking at this, I think even starts/ends with space is too aggressive. $( x.StartsWith ( y ) ) is a bit ugly but works just fine today. Maybe it has to be "has a space and is otherwise only identifier characters"? That would correctly reject $(x x) from your examples here.

public void SpaceInMiddleOfProperty(string pattern)
{
Scanner lexer = new Scanner(pattern, ParserOptions.AllowProperties);
AdvanceToScannerError(lexer);
Assert.Equal("IllFormedPropertySpaceInCondition", lexer.GetErrorResource());
lexer._errorState.ShouldBeFalse();
}

[Fact]
Expand Down
23 changes: 13 additions & 10 deletions src/Build/Evaluation/Conditionals/Scanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal sealed class Scanner
private string _expression;
private int _parsePoint;
private Token _lookahead;
private bool _errorState;
internal bool _errorState;
private int _errorPosition;
// What we found instead of what we were looking for
private string _unexpectedlyFound = null;
Expand Down Expand Up @@ -321,36 +321,39 @@ private string ParsePropertyOrItemMetadata()
private static bool ScanForPropertyExpressionEnd(string expression, int index, out int indexResult)
{
int nestLevel = 0;
bool whitespaceCheck = false;

unsafe
{
fixed (char* pchar = expression)
{
if (expression.Length > 1 && pchar[0] == '(' && char.IsWhiteSpace(pchar[1]) && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10))
Copy link
Member

Choose a reason for hiding this comment

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

is the first character in the expression guaranteed to be a '('?

Should this maybe move under the `if (character == '(')?

Something like:

                        if (character == '(')
                        {
                             if (nestLevel == 0 && index + 1 > expression.Length && pchar[index + 1] == '(' && char.IsWhiteSpace(pchar[1]) && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10))
                              {
                                    indexResult = 1;
                                     return false;
                              }
                            nestLevel++;
                        }

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes

{
indexResult = 1;
return false;
}

while (index < expression.Length)
{
char character = pchar[index];
if (character == '(')
{
nestLevel++;
whitespaceCheck = true;
}
else if (character == ')')
{
nestLevel--;
whitespaceCheck = false;
}
else if (whitespaceCheck && char.IsWhiteSpace(character) && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10))
{
indexResult = index;
return false;
}

// We have reached the end of the parenthesis nesting
// this should be the end of the property expression
// If it is not then the calling code will determine that
if (nestLevel == 0)
{
if (index > 0 && char.IsWhiteSpace(pchar[index - 1]) && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10))
{
indexResult = index - 1;
return false;
}

indexResult = index;
return true;
}
Expand Down