Skip to content

Commit 4ddba76

Browse files
JanProvaznikCopilot
andcommitted
Fix chained item function empty result comparison in conditions (#12901)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JanProvaznik <25267098+JanProvaznik@users.noreply.github.com>
1 parent 0cbb923 commit 4ddba76

File tree

2 files changed

+42
-8
lines changed

2 files changed

+42
-8
lines changed

src/Build.UnitTests/Evaluation/Expander_Tests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5344,5 +5344,35 @@ public void PropertyFunctionRegisterBuildCheck()
53445344
logger.AllBuildEvents.Count.ShouldBe(1);
53455345
}
53465346
}
5347+
5348+
/// <summary>
5349+
/// Test for issue where chained item functions with empty results incorrectly evaluate as non-empty in conditions
5350+
/// </summary>
5351+
[Fact]
5352+
public void ChainedItemFunctionEmptyResultInCondition()
5353+
{
5354+
string content = @"
5355+
<Project>
5356+
<Target Name='Test'>
5357+
<ItemGroup>
5358+
<TestItem Include='Test1' Foo='Bar' />
5359+
<TestItem Include='Test2' />
5360+
</ItemGroup>
5361+
5362+
<!-- This should be empty because Test1 has Foo='Bar', not 'Baz' -->
5363+
<PropertyGroup Condition=""'@(TestItem->WithMetadataValue('Identity', 'Test1')->WithMetadataValue('Foo', 'Baz'))' == ''"">
5364+
<EmptyResult>TRUE</EmptyResult>
5365+
</PropertyGroup>
5366+
5367+
<Message Text='EmptyResult=$(EmptyResult)' Importance='high' />
5368+
</Target>
5369+
</Project>
5370+
";
5371+
5372+
MockLogger log = Helpers.BuildProjectWithNewOMExpectSuccess(content);
5373+
5374+
// The chained WithMetadataValue should return empty, so the condition should be true and EmptyResult should be set
5375+
log.AssertLogContains("EmptyResult=TRUE");
5376+
}
53475377
}
53485378
}

src/Build/Evaluation/Expander.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2065,21 +2065,25 @@ internal static List<KeyValuePair<string, S>> Transform<S>(
20652065
break;
20662066
}
20672067

2068+
// If we have another transform, swap the source and transform lists.
2069+
if (i < captures.Count - 1)
2070+
{
2071+
(transformedItems, sourceItems) = (sourceItems, transformedItems);
2072+
transformedItems.Clear();
2073+
}
2074+
}
2075+
2076+
// Check for break on non-empty only after ALL transforms are complete
2077+
if ((options & ExpanderOptions.BreakOnNotEmpty) != 0)
2078+
{
20682079
foreach (KeyValuePair<string, S> itemTuple in transformedItems)
20692080
{
2070-
if (!string.IsNullOrEmpty(itemTuple.Key) && (options & ExpanderOptions.BreakOnNotEmpty) != 0)
2081+
if (!string.IsNullOrEmpty(itemTuple.Key))
20712082
{
20722083
brokeEarly = true;
20732084
return transformedItems; // break out early
20742085
}
20752086
}
2076-
2077-
// If we have another transform, swap the source and transform lists.
2078-
if (i < captures.Count - 1)
2079-
{
2080-
(transformedItems, sourceItems) = (sourceItems, transformedItems);
2081-
transformedItems.Clear();
2082-
}
20832087
}
20842088

20852089
brokeEarly = false;

0 commit comments

Comments
 (0)