Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

SONARJAVA-5338 Fix variable owner in the case of lambdas #5058

Merged
merged 4 commits into from
Apr 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
Expand Down Expand Up @@ -146,6 +147,23 @@ Object objectToObject2(Object o) {
}
}

class VarCanBeUsedInLambdas {
public static final Comparator<Integer> COMPARATOR = (e1, e2) -> {
int compare = Integer.compare(e1, e2); // Noncompliant {{Declare this local variable with "var" instead.}}
return compare != 0 ? compare : Integer.compare(e2, e1);
};

public static int foo() {
java.util.function.IntSupplier supplier = () -> {
var sum = 0;
for (int i = 0; i < 10; i++) { // Noncompliant {{Declare this local variable with "var" instead.}}
sum += i;
}
return sum;
};
return supplier.getAsInt();
}
}

class Abc {
static Abc getAbc() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ IMethodBinding methodBinding() {
return (IMethodBinding) binding;
}

public boolean isLambda() {
return methodBinding().getDeclaringMember() != null;
}

@Override
public List<Type> parameterTypes() {
if (parameterTypes == null) {
Expand Down Expand Up @@ -208,4 +212,10 @@ public MethodTree declaration() {
public boolean isNativeMethod() {
return !isUnknown() && Modifier.isNative(binding.getModifiers());
}

/** This is for debugging and doesn't follow a guaranteed format. */
@Override
public String toString() {
return binding.getKey();
}
}
11 changes: 5 additions & 6 deletions java-frontend/src/main/java/org/sonar/java/model/JSymbol.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,7 @@ private static boolean areEqualVariables(JSymbol thisVariableSymbol, JSymbol oth
private static boolean areEqualMethods(JSymbol thisMethodSymbol, JSymbol otherMethodSymbol) {
IMethodBinding thisBinding = (IMethodBinding) thisMethodSymbol.binding;
IMethodBinding otherBinding = (IMethodBinding) otherMethodSymbol.binding;
return thisMethodSymbol.name().equals(otherMethodSymbol.name())
&& thisMethodSymbol.owner().equals(otherMethodSymbol.owner())
&& Arrays.equals(thisBinding.getParameterTypes(), otherBinding.getParameterTypes())
&& Arrays.equals(thisBinding.getTypeParameters(), otherBinding.getTypeParameters())
&& Arrays.equals(thisBinding.getTypeArguments(), otherBinding.getTypeArguments());
return Objects.equals(thisBinding.getKey(), otherBinding.getKey());
}

@Override
Expand Down Expand Up @@ -194,7 +190,6 @@ private Symbol variableOwner(IVariableBinding variableBinding) {
if (!variableBinding.isRecordComponent()) {
IMethodBinding declaringMethod = variableBinding.getDeclaringMethod();
if (declaringMethod != null) {
// local variable
return sema.methodSymbol(declaringMethod);
}
ITypeBinding declaringClass = variableBinding.getDeclaringClass();
Expand All @@ -203,6 +198,10 @@ private Symbol variableOwner(IVariableBinding variableBinding) {
return sema.typeSymbol(declaringClass);
}
}
return ownerOfRecordComponentConstant(variableBinding);
}

private Symbol ownerOfRecordComponentConstant(IVariableBinding variableBinding) {
Tree node = sema.declarations.get(variableBinding);
if (node == null) {
// array.length
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.semantic.Type;
import org.sonar.plugins.java.api.tree.ExpressionStatementTree;
import org.sonar.plugins.java.api.tree.LambdaExpressionTree;
import org.sonar.plugins.java.api.tree.MethodInvocationTree;
import org.sonar.plugins.java.api.tree.StatementTree;
import org.sonar.plugins.java.api.tree.VariableTree;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
Expand Down Expand Up @@ -498,4 +502,25 @@ void unknown_method_is_not_native() {
}
}

@Test
void isLambda() {
JavaTree.CompilationUnitTreeImpl cu = test("""
class A {
void foo() { }
java.util.function.IntConsumer bar = i -> { };
}
""");
ClassTreeImpl a = firstClass(cu);

MethodTreeImpl foo = firstMethod(a);
var fooSymbol = (JMethodSymbol) foo.symbol();
assertFalse(fooSymbol.isLambda());
assertThat(fooSymbol.toString()).isNotEmpty();

var barAssignement = (VariableTree) a.members().get(1);
var initializer = (LambdaExpressionTree) barAssignement.initializer();
var lambdaSymbol = (JMethodSymbol) initializer.symbol();
assertTrue(lambdaSymbol.isLambda());
assertThat(lambdaSymbol.toString()).isNotEmpty();
}
}
Loading