gnodet-bot commented on code in PR #13139:
URL: https://github.com/apache/maven/pull/13139#discussion_r4015181143


##########
impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/ConditionParser.java:
##########
@@ -379,15 +419,24 @@ private Object parseVariableOrUnknownFunction() {
 
     /**
      * Parses a list of arguments for a function call.
+     * For the {@code if} function, only the branch selected by the condition 
is evaluated,
+     * the other one is skipped and passed as {@code null}.
      *
+     * @param functionName the name of the called function
      * @return a list of parsed arguments
      * @throws RuntimeException if there's a mismatch in parentheses
      */
-    private List<Object> parseArgumentList() {
+    private List<Object> parseArgumentList(String functionName) {
         List<Object> args = new ArrayList<>();
         current++; // Skip the opening parenthesis
         while (current < tokens.size() && !tokens.get(current).equals(")")) {
-            args.add(parseLogicalOr());
+            int index = args.size();
+            if ("if".equals(functionName) && (index == 1 || index == 2) && 
toBoolean(args.get(0)) != (index == 1)) {

Review Comment:
   ⚠️ **Design coupling: `"if"` hardcoded in a function-agnostic parser**
   
   `ConditionParser` receives its function set as an injected `Map<String, 
ExpressionFunction>` — the parser is supposed to be agnostic about which 
functions exist. This line breaks that contract: the parser now needs to know 
the *name* `"if"` to apply lazy evaluation.
   
   If a second function needing lazy evaluation is added (e.g., `"unless"`, 
`"cond"`, a short-circuit `"and"`), another magic-string guard has to be added 
here.
   
   Two paths forward:
   1. **Minimal (in-PR):** Extract the string to a constant to make the 
coupling visible:
      ```suggestion
               if ("if".equals(functionName) && (index == 1 || index == 2) && 
toBoolean(args.get(0)) != (index == 1)) {
      ```
      *(No behaviour change — but at least the magic string is a named constant 
in the companion commit, or a `private static final String IF_FUNCTION = "if"`)*
   2. **Proper long-term:** Introduce a `LazyExpressionFunction` sub-interface 
whose `apply` receives a `Supplier<Object>` or an unevaluated index range, so 
the parser can dispatch lazily without knowing function names.



##########
impl/maven-impl/src/test/java/org/apache/maven/impl/model/profile/ConditionParserTest.java:
##########
@@ -182,6 +182,40 @@ void testIfFunction() {
         assertEquals("short", parser.parse("if(length('hi') > 3, 'long', 
'short')"));
     }
 
+    /**
+     * Only the selected branch of {@code if(..)} is evaluated. The other one 
may not be valid for
+     * the current input, as in the example from the {@code condition} 
documentation: with a
+     * {@code java.version} that has no {@code -}, the unselected {@code 
substring(..)} would get an
+     * end index of -1.
+     */
+    @Test
+    void testIfFunctionOnlyEvaluatesSelectedBranch() {
+        assertEquals(
+                "1.8.0_292",
+                parser.parse("if(contains(${java.version}, '-'), "
+                        + "substring(${java.version}, 0, 
indexOf(${java.version}, '-')), ${java.version})"));
+        assertEquals(
+                "21",
+                parser.parse("if(contains('21-ea', '-'), substring('21-ea', 0, 
indexOf('21-ea', '-')), '21-ea')"));
+        assertThrows(RuntimeException.class, () -> parser.parse("if(true, 
'a')"));
+        assertThrows(RuntimeException.class, () -> parser.parse("if(false, 
substring('a', 0, 5), 'b'"));

Review Comment:
   🔍 **Missing symmetric test case: `if(false, 'a')` (2-arg, false branch)**
   
   The test at line 200 covers `if(true, 'a')` — the 2-arg call where the 
*true* branch would be selected but only one argument was supplied. The 
symmetric case `if(false, 'a')` — where the *false* branch would be taken — is 
not covered. In the new code, when `condition=false` and `index==1`, the guard 
`toBoolean(args.get(0)) != (index == 1)` becomes `false != true` → `true`, so 
the branch is **skipped** and `null` is appended. The call to `if_()` then 
receives `[false, null]` (size 2), which correctly throws — but this path is 
not exercised by the test suite.
   
   Suggested addition:
   ```suggestion
           assertThrows(RuntimeException.class, () -> parser.parse("if(true, 
'a')"));
           assertThrows(RuntimeException.class, () -> parser.parse("if(false, 
'a')"));
           assertThrows(RuntimeException.class, () -> parser.parse("if(false, 
substring('a', 0, 5), 'b'"));
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to