Fix NPE in VarWithPrimitive when lambda parameters have inferred primitive types#5868
Fix NPE in VarWithPrimitive when lambda parameters have inferred primitive types#5868HarshMehta112 wants to merge 3 commits into
VarWithPrimitive when lambda parameters have inferred primitive types#5868Conversation
…e type replacement Signed-off-by: Harsh Mehta <harshmehta010102@gmail.com>
|
Thanks for the fix! Could you also add a couple more test cases:
|
Signed-off-by: Harsh Mehta <harshmehta010102@gmail.com>
cushon
left a comment
There was a problem hiding this comment.
It looks like the author information for the commits are from two different github accounts and emails, and there is only a CLA on file for one of them
Can you sign the CLA for the other account or update the author information for the commits if it is incorrect?
| @Test | ||
| public void annotatedVarLocalVariable() { | ||
| // The token scan finds two 'var' tokens (@A(var=0) attribute + type keyword) and safely | ||
| // returns no fix rather than risk replacing the annotation attribute instead of the type. |
There was a problem hiding this comment.
The CI shows this test fails on JDK 27 because there's now an AST node for the var type, so the replacement succeeds
Can you split this test into separate tests for JDK < 27 and JDK >= 27, and use assume().that(Runtime.version().feature()).{isAtLeast,isLessThan}(27) to run the tests on the different JDK versions?
There was a problem hiding this comment.
Done — I split the test into JDK < 27 and JDK ≥ 27 variants using assume().that(Runtime.version().feature()).isLessThan(27) and isAtLeast(27).
Signed-off-by: Harsh Mehta <harshmehta010102@gmail.com>
…imitive types ## Problem Fixes #5857 `VarWithPrimitive` throws a `NullPointerException` when a lambda has an implicit parameter whose inferred type is a primitive: ```java byte[] bar = new byte[6]; Map<Byte, List<Byte>> indicesMap = IntStream.range(0, 6) .mapToObj(n -> n) .collect(Collectors.groupingBy( n -> bar[n], Collectors.mapping( n -> (byte) n.intValue(), Collectors.toList()))); // Exception: java.lang.NullPointerException: Cannot invoke "com.sun.tools.javac.tree.JCTree.getStartPosition()" because "tree" is null ``` ## Root Cause `hasImplicitType()` returns `true` for both: - `var` declarations - Implicit lambda parameters (when `VariableTree.getType()` is `null`, per JDK-8268850) `VarWithPrimitive` used `hasImplicitType()` as its guard, so lambda parameters with inferred primitive types passed the check and reached `replaceVariableType()`. Inside `replaceVariableType()`, `tree.getType()` returns `null` for these parameters. `hasExplicitSource(null, state)` was then called unconditionally, which invokes: ```java getStartPosition(null) ``` resulting in a `NullPointerException`. ## Fix Add a null guard for `type` in `SuggestedFixes.replaceVariableType()` at the actual crash site. If `type == null`: - Skip the `hasExplicitSource()` call. - Fall through to `getStartPosition(tree)`. The subsequent token scan finds no `var` keyword for implicit lambda parameters and correctly returns `Optional.empty()`, producing `NO_MATCH` without throwing an exception. This is the minimal fix because: - It addresses the actual crash site. - It is defensive against any other callers that may pass a `VariableTree` with a null type. ## Tests ### `implicitLambdaParameter_noMatch` Regression test reproducing the exact code from the bug report: - Single-parameter lambdas - Inferred `int` and `byte` types - Verifies `NO_MATCH` and no exception ### `multiParamImplicitLambda_noMatch` Tests a multi-parameter implicit lambda: ```java IntBinaryOperator op = (a, b) -> a + b; ``` - Both parameters inferred as `int` - Verifies `NO_MATCH` and no exception ### `explicitLambdaParam_noMatch` Tests an explicit lambda parameter: ```java (int n) -> n ``` - `hasImplicitType()` returns `false` - Verifies no match is reported ### `enhancedForLoop` Tests: ```java for (var x : intArray) { // ... } ``` Expected fix: ```java for (int x : intArray) { // ... } ``` ### `forLoopInitializer` Tests: ```java for (var i = 0; i < 10; i++) { // ... } ``` Expected fix: ```java for (int i = 0; i < 10; i++) { // ... } ``` Fixes #5868 FUTURE_COPYBARA_INTEGRATE_REVIEW=#5868 from HarshMehta112:master 5881069 PiperOrigin-RevId: 931039338
…imitive types ## Problem Fixes #5857 `VarWithPrimitive` throws a `NullPointerException` when a lambda has an implicit parameter whose inferred type is a primitive: ```java byte[] bar = new byte[6]; Map<Byte, List<Byte>> indicesMap = IntStream.range(0, 6) .mapToObj(n -> n) .collect(Collectors.groupingBy( n -> bar[n], Collectors.mapping( n -> (byte) n.intValue(), Collectors.toList()))); // Exception: java.lang.NullPointerException: Cannot invoke "com.sun.tools.javac.tree.JCTree.getStartPosition()" because "tree" is null ``` ## Root Cause `hasImplicitType()` returns `true` for both: - `var` declarations - Implicit lambda parameters (when `VariableTree.getType()` is `null`, per JDK-8268850) `VarWithPrimitive` used `hasImplicitType()` as its guard, so lambda parameters with inferred primitive types passed the check and reached `replaceVariableType()`. Inside `replaceVariableType()`, `tree.getType()` returns `null` for these parameters. `hasExplicitSource(null, state)` was then called unconditionally, which invokes: ```java getStartPosition(null) ``` resulting in a `NullPointerException`. ## Fix Add a null guard for `type` in `SuggestedFixes.replaceVariableType()` at the actual crash site. If `type == null`: - Skip the `hasExplicitSource()` call. - Fall through to `getStartPosition(tree)`. The subsequent token scan finds no `var` keyword for implicit lambda parameters and correctly returns `Optional.empty()`, producing `NO_MATCH` without throwing an exception. This is the minimal fix because: - It addresses the actual crash site. - It is defensive against any other callers that may pass a `VariableTree` with a null type. ## Tests ### `implicitLambdaParameter_noMatch` Regression test reproducing the exact code from the bug report: - Single-parameter lambdas - Inferred `int` and `byte` types - Verifies `NO_MATCH` and no exception ### `multiParamImplicitLambda_noMatch` Tests a multi-parameter implicit lambda: ```java IntBinaryOperator op = (a, b) -> a + b; ``` - Both parameters inferred as `int` - Verifies `NO_MATCH` and no exception ### `explicitLambdaParam_noMatch` Tests an explicit lambda parameter: ```java (int n) -> n ``` - `hasImplicitType()` returns `false` - Verifies no match is reported ### `enhancedForLoop` Tests: ```java for (var x : intArray) { // ... } ``` Expected fix: ```java for (int x : intArray) { // ... } ``` ### `forLoopInitializer` Tests: ```java for (var i = 0; i < 10; i++) { // ... } ``` Expected fix: ```java for (int i = 0; i < 10; i++) { // ... } ``` Fixes #5868 FUTURE_COPYBARA_INTEGRATE_REVIEW=#5868 from HarshMehta112:master 5881069 PiperOrigin-RevId: 931039338
…imitive types ## Problem Fixes #5857 `VarWithPrimitive` throws a `NullPointerException` when a lambda has an implicit parameter whose inferred type is a primitive: ```java byte[] bar = new byte[6]; Map<Byte, List<Byte>> indicesMap = IntStream.range(0, 6) .mapToObj(n -> n) .collect(Collectors.groupingBy( n -> bar[n], Collectors.mapping( n -> (byte) n.intValue(), Collectors.toList()))); // Exception: java.lang.NullPointerException: Cannot invoke "com.sun.tools.javac.tree.JCTree.getStartPosition()" because "tree" is null ``` ## Root Cause `hasImplicitType()` returns `true` for both: - `var` declarations - Implicit lambda parameters (when `VariableTree.getType()` is `null`, per JDK-8268850) `VarWithPrimitive` used `hasImplicitType()` as its guard, so lambda parameters with inferred primitive types passed the check and reached `replaceVariableType()`. Inside `replaceVariableType()`, `tree.getType()` returns `null` for these parameters. `hasExplicitSource(null, state)` was then called unconditionally, which invokes: ```java getStartPosition(null) ``` resulting in a `NullPointerException`. ## Fix Add a null guard for `type` in `SuggestedFixes.replaceVariableType()` at the actual crash site. If `type == null`: - Skip the `hasExplicitSource()` call. - Fall through to `getStartPosition(tree)`. The subsequent token scan finds no `var` keyword for implicit lambda parameters and correctly returns `Optional.empty()`, producing `NO_MATCH` without throwing an exception. This is the minimal fix because: - It addresses the actual crash site. - It is defensive against any other callers that may pass a `VariableTree` with a null type. ## Tests ### `implicitLambdaParameter_noMatch` Regression test reproducing the exact code from the bug report: - Single-parameter lambdas - Inferred `int` and `byte` types - Verifies `NO_MATCH` and no exception ### `multiParamImplicitLambda_noMatch` Tests a multi-parameter implicit lambda: ```java IntBinaryOperator op = (a, b) -> a + b; ``` - Both parameters inferred as `int` - Verifies `NO_MATCH` and no exception ### `explicitLambdaParam_noMatch` Tests an explicit lambda parameter: ```java (int n) -> n ``` - `hasImplicitType()` returns `false` - Verifies no match is reported ### `enhancedForLoop` Tests: ```java for (var x : intArray) { // ... } ``` Expected fix: ```java for (int x : intArray) { // ... } ``` ### `forLoopInitializer` Tests: ```java for (var i = 0; i < 10; i++) { // ... } ``` Expected fix: ```java for (int i = 0; i < 10; i++) { // ... } ``` Fixes #5868 FUTURE_COPYBARA_INTEGRATE_REVIEW=#5868 from HarshMehta112:master 5881069 PiperOrigin-RevId: 931039338
…imitive types ## Problem Fixes #5857 `VarWithPrimitive` throws a `NullPointerException` when a lambda has an implicit parameter whose inferred type is a primitive: ```java byte[] bar = new byte[6]; Map<Byte, List<Byte>> indicesMap = IntStream.range(0, 6) .mapToObj(n -> n) .collect(Collectors.groupingBy( n -> bar[n], Collectors.mapping( n -> (byte) n.intValue(), Collectors.toList()))); // Exception: java.lang.NullPointerException: Cannot invoke "com.sun.tools.javac.tree.JCTree.getStartPosition()" because "tree" is null ``` ## Root Cause `hasImplicitType()` returns `true` for both: - `var` declarations - Implicit lambda parameters (when `VariableTree.getType()` is `null`, per JDK-8268850) `VarWithPrimitive` used `hasImplicitType()` as its guard, so lambda parameters with inferred primitive types passed the check and reached `replaceVariableType()`. Inside `replaceVariableType()`, `tree.getType()` returns `null` for these parameters. `hasExplicitSource(null, state)` was then called unconditionally, which invokes: ```java getStartPosition(null) ``` resulting in a `NullPointerException`. ## Fix Add a null guard for `type` in `SuggestedFixes.replaceVariableType()` at the actual crash site. If `type == null`: - Skip the `hasExplicitSource()` call. - Fall through to `getStartPosition(tree)`. The subsequent token scan finds no `var` keyword for implicit lambda parameters and correctly returns `Optional.empty()`, producing `NO_MATCH` without throwing an exception. This is the minimal fix because: - It addresses the actual crash site. - It is defensive against any other callers that may pass a `VariableTree` with a null type. ## Tests ### `implicitLambdaParameter_noMatch` Regression test reproducing the exact code from the bug report: - Single-parameter lambdas - Inferred `int` and `byte` types - Verifies `NO_MATCH` and no exception ### `multiParamImplicitLambda_noMatch` Tests a multi-parameter implicit lambda: ```java IntBinaryOperator op = (a, b) -> a + b; ``` - Both parameters inferred as `int` - Verifies `NO_MATCH` and no exception ### `explicitLambdaParam_noMatch` Tests an explicit lambda parameter: ```java (int n) -> n ``` - `hasImplicitType()` returns `false` - Verifies no match is reported ### `enhancedForLoop` Tests: ```java for (var x : intArray) { // ... } ``` Expected fix: ```java for (int x : intArray) { // ... } ``` ### `forLoopInitializer` Tests: ```java for (var i = 0; i < 10; i++) { // ... } ``` Expected fix: ```java for (int i = 0; i < 10; i++) { // ... } ``` Fixes #5868 FUTURE_COPYBARA_INTEGRATE_REVIEW=#5868 from HarshMehta112:master 5881069 PiperOrigin-RevId: 931039338
…imitive types ## Problem Fixes #5857 `VarWithPrimitive` throws a `NullPointerException` when a lambda has an implicit parameter whose inferred type is a primitive: ```java byte[] bar = new byte[6]; Map<Byte, List<Byte>> indicesMap = IntStream.range(0, 6) .mapToObj(n -> n) .collect(Collectors.groupingBy( n -> bar[n], Collectors.mapping( n -> (byte) n.intValue(), Collectors.toList()))); // Exception: java.lang.NullPointerException: Cannot invoke "com.sun.tools.javac.tree.JCTree.getStartPosition()" because "tree" is null ``` ## Root Cause `hasImplicitType()` returns `true` for both: - `var` declarations - Implicit lambda parameters (when `VariableTree.getType()` is `null`, per JDK-8268850) `VarWithPrimitive` used `hasImplicitType()` as its guard, so lambda parameters with inferred primitive types passed the check and reached `replaceVariableType()`. Inside `replaceVariableType()`, `tree.getType()` returns `null` for these parameters. `hasExplicitSource(null, state)` was then called unconditionally, which invokes: ```java getStartPosition(null) ``` resulting in a `NullPointerException`. ## Fix Add a null guard for `type` in `SuggestedFixes.replaceVariableType()` at the actual crash site. If `type == null`: - Skip the `hasExplicitSource()` call. - Fall through to `getStartPosition(tree)`. The subsequent token scan finds no `var` keyword for implicit lambda parameters and correctly returns `Optional.empty()`, producing `NO_MATCH` without throwing an exception. This is the minimal fix because: - It addresses the actual crash site. - It is defensive against any other callers that may pass a `VariableTree` with a null type. ## Tests ### `implicitLambdaParameter_noMatch` Regression test reproducing the exact code from the bug report: - Single-parameter lambdas - Inferred `int` and `byte` types - Verifies `NO_MATCH` and no exception ### `multiParamImplicitLambda_noMatch` Tests a multi-parameter implicit lambda: ```java IntBinaryOperator op = (a, b) -> a + b; ``` - Both parameters inferred as `int` - Verifies `NO_MATCH` and no exception ### `explicitLambdaParam_noMatch` Tests an explicit lambda parameter: ```java (int n) -> n ``` - `hasImplicitType()` returns `false` - Verifies no match is reported ### `enhancedForLoop` Tests: ```java for (var x : intArray) { // ... } ``` Expected fix: ```java for (int x : intArray) { // ... } ``` ### `forLoopInitializer` Tests: ```java for (var i = 0; i < 10; i++) { // ... } ``` Expected fix: ```java for (int i = 0; i < 10; i++) { // ... } ``` Fixes #5868 FUTURE_COPYBARA_INTEGRATE_REVIEW=#5868 from HarshMehta112:master 5881069 PiperOrigin-RevId: 931039338
Problem
Fixes #5857
VarWithPrimitivethrows aNullPointerExceptionwhen a lambda has an implicitparameter whose inferred type is a primitive:
Root Cause
hasImplicitType()returnstruefor both:vardeclarationsVariableTree.getType()isnull, per JDK-8268850)VarWithPrimitiveusedhasImplicitType()as its guard, so lambda parameterswith inferred primitive types passed the check and reached
replaceVariableType().Inside
replaceVariableType(),tree.getType()returnsnullfor theseparameters.
hasExplicitSource(null, state)was then called unconditionally,which invokes:
resulting in a
NullPointerException.Fix
Add a null guard for
typeinSuggestedFixes.replaceVariableType()at theactual crash site.
If
type == null:hasExplicitSource()call.getStartPosition(tree).The subsequent token scan finds no
varkeyword for implicit lambdaparameters and correctly returns
Optional.empty(), producingNO_MATCHwithout throwing an exception.
This is the minimal fix because:
VariableTreewith a null type.
Tests
implicitLambdaParameter_noMatchRegression test reproducing the exact code from the bug report:
intandbytetypesNO_MATCHand no exceptionmultiParamImplicitLambda_noMatchTests a multi-parameter implicit lambda:
intNO_MATCHand no exceptionexplicitLambdaParam_noMatchTests an explicit lambda parameter:
hasImplicitType()returnsfalseenhancedForLoopTests:
Expected fix:
forLoopInitializerTests:
Expected fix: