-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Fix boxing for registration of builtin condition methods #5970
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
Conversation
WalkthroughCondition lambdas for string comparisons now return boxed ConditionExpression values instead of raw booleans across equals/strequals/contains/starts-with/ends-with (2- and 3-parameter variants). The 3-parameter forms retain ignoreCase handling. Both the source file and its T4 template were updated; no public signatures changed. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Logger as Logger
participant Cond as Condition Lambda
participant Methods as ConditionMethods
participant Expr as ConditionExpression
Logger->>Cond: Evaluate condition(logEvent, args)
Cond->>Methods: equals/strequals/contains/starts/ends(...)
Methods-->>Cond: bool result
alt result == true
Cond-->>Expr: BoxedTrue
else result == false
Cond-->>Expr: BoxedFalse
end
Expr-->>Logger: ConditionExpression (boxed)
note over Cond,Expr: 3-arg forms keep ignoreCase behavior unchanged
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/NLog/Config/AssemblyExtensionTypes.cs (1)
392-392: Add unit tests to lock in the boxing behavior across all operators.Recommend adding tests that assert the returned objects are the canonical boxed instances. This guards against accidental re-introduction of raw bool boxing.
Example outline:
- For each of equals/strequals/contains/starts-with/ends-with (2- and 3-param variants), invoke via ConditionMethodFactory and assert
ReferenceEquals(result, NLog.Conditions.ConditionExpression.BoxedTrue/False).If helpful, I can draft these tests.
Also applies to: 400-402, 405-407, 410-412
src/NLog/Config/AssemblyExtensionTypes.tt (1)
253-253: Optional: DRY the boxing pattern in T4 to reduce repetition.To make future edits safer, consider a small T4 helper like
Box(boolExpr)that emits the ternary with the fully-qualified constants.Example:
<#+ string BoxBool(string expr) => $"{expr} ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse"; #>Then use:
factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => <#= BoxBool("NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())") #>);Also applies to: 256-258, 261-263, 266-268, 271-273
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/NLog/Config/AssemblyExtensionTypes.cs(1 hunks)src/NLog/Config/AssemblyExtensionTypes.tt(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/NLog/Config/AssemblyExtensionTypes.cs (3)
src/NLog/Config/MethodFactory.cs (3)
RegisterTwoParameters(322-330)CheckTypeAliasExists(110-110)RegisterThreeParameters(332-340)src/NLog/Conditions/ConditionMethods.cs (1)
ConditionMethods(43-120)src/NLog/Config/Factory.cs (1)
CheckTypeAliasExists(85-85)
🔇 Additional comments (4)
src/NLog/Config/AssemblyExtensionTypes.cs (2)
392-392: Good change: removes per-evaluation boxing allocations.Returning
ConditionExpression.BoxedTrue/BoxedFalsematches theFunc<..., object?>signature expected by MethodFactory and avoids creating new boxed bools on each evaluation. This should cut GC pressure in hot condition paths.Also applies to: 395-397, 400-402, 405-407, 410-412
395-397: Verifystrequalsthird-parameter default fallbackThe
Equals2method in ConditionMethods is marked with[DefaultParameterValue(false)], yet ourRegisterThreeParameters("strequals", …)logic falls back totruewhenarg3isn’t a bool. This mismatch means an expression likestrequals(a, b, "false")would unexpectedly behave as case-insensitive. If this was an intentional choice for back-compat, no change is needed; otherwise, consider defaulting tofalseto align with the method’s attribute.• Location: src/NLog/Config/AssemblyExtensionTypes.cs (lines 395–397)
• Current code:factory.GetConditionMethodFactory() .RegisterThreeParameters("strequals", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Equals2( arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);• Optional refactor to match
[DefaultParameterValue(false)]:- arg3 is bool ignoreCase ? ignoreCase : true + arg3 is bool ignoreCase ? ignoreCase : falsePlease verify whether the current
truefallback is intentional for backward compatibility or if it should be changed tofalse.src/NLog/Config/AssemblyExtensionTypes.tt (2)
253-253: Nice allocation win baked into the generator.Baking the boxing optimization into the .tt keeps the generated code consistent and prevents future regressions.
Also applies to: 256-258, 261-263, 266-268, 271-273
253-253: All condition registrations correctly use canonical BoxedTrue/BoxedFalse constantsVerified that for all “equals”, “strequals”, “contains”, “starts-with”, and “ends-with” registrations—across both the T4 template (
AssemblyExtensionTypes.tt) and the generated C# output (AssemblyExtensionTypes.cs)—the methods consistently useConditions.ConditionExpression.BoxedTrueandConditions.ConditionExpression.BoxedFalse, with no rawtrue/falseliterals remaining. The optional alias check also found no unintendedusing Conditions =directives.
| factory.GetConditionMethodFactory().RegisterOneParameter("length", (logEvent, arg1) => NLog.Conditions.ConditionMethods.Length(arg1?.ToString())); | ||
| if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("equals")) | ||
| factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())); | ||
| factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use fully-qualified NLog.Conditions.ConditionExpression (avoid relying on a missing alias).
These lambdas reference Conditions.ConditionExpression.BoxedTrue/BoxedFalse. There’s no using Conditions = NLog.Conditions; (nor an obvious global alias) in this file, while other calls here use the full NLog.Conditions.* qualification. This will fail to compile unless a project-wide alias exists. Recommend fully-qualifying to NLog.Conditions.ConditionExpression for consistency and safety.
Apply this diff:
- factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString()) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterTwoParameters("strequals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterThreeParameters("strequals", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterTwoParameters("strequals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString()) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterThreeParameters("strequals", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterTwoParameters("contains", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterThreeParameters("contains", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterTwoParameters("contains", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString()) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterThreeParameters("contains", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterTwoParameters("starts-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterThreeParameters("starts-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterTwoParameters("starts-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString()) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterThreeParameters("starts-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterTwoParameters("ends-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterTwoParameters("ends-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString()) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);Also applies to: 395-397, 400-402, 405-407, 410-412
🤖 Prompt for AI Agents
In src/NLog/Config/AssemblyExtensionTypes.cs around lines 392 (and similarly
395-397, 400-402, 405-407, 410-412), the lambda expressions use
Conditions.ConditionExpression.BoxedTrue/BoxedFalse without a local alias for
Conditions which can break compilation; update each occurrence to use the
fully-qualified NLog.Conditions.ConditionExpression.BoxedTrue and
NLog.Conditions.ConditionExpression.BoxedFalse so they match the surrounding
code and avoid relying on a project-wide alias.
| factory.GetConditionMethodFactory().RegisterOneParameter("length", (logEvent, arg1) => NLog.Conditions.ConditionMethods.Length(arg1?.ToString())); | ||
| if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("equals")) | ||
| factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())); | ||
| factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Template should also fully-qualify ConditionExpression to avoid alias dependency.
Same concern as in the generated .cs: prefer NLog.Conditions.ConditionExpression to Conditions.ConditionExpression to prevent compile-time resolution issues if no alias exists. Keeping the T4 authoritative prevents future regressions.
Apply this diff:
- factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString()) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterTwoParameters("strequals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterThreeParameters("strequals", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterTwoParameters("strequals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString()) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterThreeParameters("strequals", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterTwoParameters("contains", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterThreeParameters("contains", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterTwoParameters("contains", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString()) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterThreeParameters("contains", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterTwoParameters("starts-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterThreeParameters("starts-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterTwoParameters("starts-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString()) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterThreeParameters("starts-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterTwoParameters("ends-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString()) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
- factory.GetConditionMethodFactory().RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? Conditions.ConditionExpression.BoxedTrue : Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterTwoParameters("ends-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString()) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);
+ factory.GetConditionMethodFactory().RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true) ? NLog.Conditions.ConditionExpression.BoxedTrue : NLog.Conditions.ConditionExpression.BoxedFalse);Also applies to: 256-258, 261-263, 266-268, 271-273
🤖 Prompt for AI Agents
In src/NLog/Config/AssemblyExtensionTypes.tt around lines 253 (and similarly at
256-258, 261-263, 266-268, 271-273), the generated template uses the short alias
Conditions.ConditionExpression; update each occurrence to use the full type name
NLog.Conditions.ConditionExpression (e.g., replace
Conditions.ConditionExpression.BoxedTrue/BoxedFalse with
NLog.Conditions.ConditionExpression.BoxedTrue/BoxedFalse) so the generated .cs
files do not rely on a using alias and will compile even if the alias is absent.



No description provided.