Skip to content

Conversation

@JanProvaznik
Copy link
Member

Fixes #12877

Context

Chained item functions incorrectly evaluate as non-empty in conditions when the final result is empty. For example:

<ItemGroup>
  <TestItem Include="Test1" Foo="Bar" />
</ItemGroup>
<Message Text="Success" 
  Condition="'@(TestItem->WithMetadataValue('Identity','Test1')->WithMetadataValue('Foo','Baz'))' == ''" />

The first transform matches Test1, but the second filters it out (Foo="Bar" != "Baz"). The final result is empty, yet the condition fails.

Changes Made

  • src/Build/Evaluation/Expander.cs

    • Moved BreakOnNotEmpty check in Transform method to execute after all chained transforms complete, not after each intermediate transform
    • Previously checked after each transform at line ~2024, causing early bailout on intermediate non-empty results
    • Now checks only the final result at line ~2032-2043
  • src/Build.UnitTests/Evaluation/Expander_Tests.cs

    • Added ChainedItemFunctionEmptyResultInCondition test verifying the fix

Testing

  • Unit test added to validate the specific scenario.
  • Full test suite and manual verification pending.

Notes

  • The bug only manifested when using BreakOnNotEmpty optimization (condition evaluation path), not in normal property expansion.

Copilot AI and others added 3 commits December 5, 2025 14:18
The issue is that when chaining item functions like:
@(TestItem->WithMetadataValue('Identity', 'Test1')->WithMetadataValue('Foo', 'Baz'))

The result incorrectly evaluates as non-empty when compared in a condition, even though the actual result is empty.

Initial hypothesis was that the BreakOnNotEmpty optimization in Transform() was breaking early after the first transform. However, testing revealed that the Transform() method is NOT being called at all for chained transforms with BreakOnNotEmpty option.

This suggests the bug is elsewhere - possibly in how ExpressionShredder parses chained transforms or in how they're evaluated separately.

Co-authored-by: JanProvaznik <25267098+JanProvaznik@users.noreply.github.com>
When chaining item functions like @(Item->Func1()->Func2()), and the final result is empty, the condition comparison with empty string was incorrectly evaluating to false.

The bug was in the Transform method in Expander.cs. The original code checked for BreakOnNotEmpty after EACH intermediate transform, causing it to break early when an intermediate result was non-empty, even if subsequent transforms would filter to an empty result.

The fix moves the BreakOnNotEmpty check to after ALL transforms in the chain complete, ensuring we only check the final result, not intermediate ones.

Added a unit test to verify the fix works correctly.

Co-authored-by: JanProvaznik <25267098+JanProvaznik@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 9, 2025 12:10
@JanProvaznik JanProvaznik self-assigned this Dec 9, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a bug (#12877) where chained item functions incorrectly evaluated as non-empty in conditions when the final result was actually empty. The issue occurred because the BreakOnNotEmpty optimization was checking intermediate results after each transform in the chain, rather than checking only the final result after all transforms completed.

Key changes:

  • Modified the Transform method in Expander.cs to delay the BreakOnNotEmpty check until after all chained transforms complete
  • Added a test case to verify chained item functions with empty results are correctly evaluated in conditions

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Build/Evaluation/Expander.cs Moved BreakOnNotEmpty optimization check from inside the transform loop to after all transforms complete, ensuring correct evaluation of chained item functions
src/Build.UnitTests/Evaluation/Expander_Tests.cs Added test case ChainedItemFunctionEmptyResultInCondition to verify the bug fix with a scenario using chained WithMetadataValue functions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

When chaining item functions and comparing the result to empty string, the comparison will return false incorrectly.

2 participants