Skip to content

Commit

Permalink
detect Maps for leaked collections Feuermagier#625
Browse files Browse the repository at this point in the history
  • Loading branch information
Luro02 committed Dec 9, 2024
1 parent c2d06de commit b613b11
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.declaration.CtTypedElement;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.CtScanner;
import spoon.reflect.visitor.filter.TypeFilter;
Expand Down Expand Up @@ -81,7 +80,7 @@
})
public class LeakedCollectionCheck extends IntegratedCheck {
private static boolean isMutableType(CtTypedElement<?> ctTypedElement) {
return ctTypedElement.getType().isArray() || TypeUtil.isSubtypeOf(ctTypedElement.getType(), java.util.Collection.class);
return ctTypedElement.getType().isArray() || TypeUtil.isSubtypeOf(ctTypedElement.getType(), java.util.Collection.class) || TypeUtil.isSubtypeOf(ctTypedElement.getType(), java.util.Map.class);
}

/**
Expand All @@ -98,7 +97,7 @@ private static boolean canBeMutated(CtField<?> ctVariable) {
return true;
}

if (!TypeUtil.isSubtypeOf(ctVariable.getType(), java.util.Collection.class)) {
if (!TypeUtil.isSubtypeOf(ctVariable.getType(), java.util.Collection.class) && !TypeUtil.isSubtypeOf(ctVariable.getType(), java.util.Map.class)) {
// not a collection
return false;
}
Expand Down Expand Up @@ -130,7 +129,7 @@ private boolean isMutableExpression(CtExpression<?> ctExpression) {
}

// we only care about arrays and collections for now
if (!TypeUtil.isSubtypeOf(ctExpression.getType(), java.util.Collection.class)) {
if (!TypeUtil.isSubtypeOf(ctExpression.getType(), java.util.Collection.class) && !TypeUtil.isSubtypeOf(ctExpression.getType(), java.util.Map.class)) {
// not a collection
return false;
}
Expand Down Expand Up @@ -412,24 +411,6 @@ private static CtReturn<?> createCtReturn(CtExpression<?> ctExpression) {
return ctReturn.setReturnedExpression((CtExpression) ctExpression);
}

// The generated accessor method of a record does not have a real return statement.
// This method fixes that by creating the return statement.
private static CtMethod<?> fixRecordAccessor(CtRecord ctRecord, CtMethod<?> ctMethod) {
// TODO: remove when https://github.com/INRIA/spoon/pull/5801 is merged.
Factory factory = ctMethod.getFactory();
CtMethod<?> result = ctMethod.clone();
CtFieldRead ctFieldRead = factory.createFieldRead();

ctFieldRead.setTarget(null);
ctFieldRead.setVariable(ctRecord.getField(ctMethod.getSimpleName()).getReference());
ctFieldRead.setType(result.getType());

result.setBody(createCtReturn(ctFieldRead));
result.setParent(ctRecord);

return result;
}

@Override
protected void check(StaticAnalysis staticAnalysis) {
staticAnalysis.getModel().getRootPackage().accept(new CtScanner() {
Expand All @@ -439,10 +420,6 @@ private <T> void checkCtType(CtType<T> ctType) {
}

for (CtTypeMember ctTypeMember : ctType.getTypeMembers()) {
if (ctType instanceof CtRecord ctRecord && ctTypeMember instanceof CtMethod<?> ctMethod && ctMethod.isImplicit()) {
ctTypeMember = fixRecordAccessor(ctRecord, ctMethod);
}

switch (ctTypeMember) {
case CtConstructor<?> ctConstructor -> checkCtExecutableAssign(ctConstructor);
case CtMethod<?> ctMethod -> {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ public static void main(String[] args) {}
}

@Test
@Disabled("Some spoon bug in 11.1.1-SNAPSHOT")
void testRecordStaticField() throws LinterException, IOException {
ProblemIterator problems = this.checkIterator(StringSourceInfo.fromSourceString(
JavaVersion.JAVA_17,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,27 @@ public record Zoo(String name, Collection<String> animals) {
problems.assertExhausted();
}

@Test
void testCompactConstructorImmutableMap() throws IOException, LinterException {
ProblemIterator problems = this.checkIterator(StringSourceInfo.fromSourceString(
JavaVersion.JAVA_17,
"Zoo",
"""
import java.util.Map;
import java.util.Collection;
import java.util.HashMap;
public record Zoo(String name, Map<String, String> animals) {
public Zoo {
animals = Map.copyOf(animals);
}
}
"""
), PROBLEM_TYPES);

problems.assertExhausted();
}

@Test
void testCompactConstructorLeakedAssign() throws IOException, LinterException {
ProblemIterator problems = this.checkIterator(StringSourceInfo.fromSourceString(
Expand Down Expand Up @@ -962,6 +983,32 @@ public Collection<String> animals() {
problems.assertExhausted();
}

@Test
void testCompactConstructorLeakedAssignMap() throws IOException, LinterException {
ProblemIterator problems = this.checkIterator(StringSourceInfo.fromSourceString(
JavaVersion.JAVA_17,
"Zoo",
"""
import java.util.Map;
import java.util.Collection;
import java.util.HashMap;
public record Zoo(String name, Map<String, String> animals) {
public Zoo {
animals = animals;
}
public Map<String, String> animals() {
return new HashMap<>(animals);
}
}
"""
), PROBLEM_TYPES);

assertEqualsLeakedConstructor(problems.next(), "Zoo(String, Map<String, String>)", "animals");

problems.assertExhausted();
}

@Test
void testCompactConstructorCopied() throws IOException, LinterException {
Expand All @@ -986,6 +1033,29 @@ public record Zoo(String name, Collection<String> animals) {
problems.assertExhausted();
}

@Test
void testCompactConstructorCopiedMap() throws IOException, LinterException {
ProblemIterator problems = this.checkIterator(StringSourceInfo.fromSourceString(
JavaVersion.JAVA_17,
"Zoo",
"""
import java.util.Map;
import java.util.Collection;
import java.util.HashMap;
public record Zoo(String name, Map<String, String> animals) {
public Zoo {
animals = new HashMap<>(animals);
}
}
"""
), PROBLEM_TYPES);

assertEqualsLeakedReturn(problems.next(), "animals", "animals");

problems.assertExhausted();
}


@Test
void testSelfAssignment() throws IOException, LinterException {
Expand Down
10 changes: 9 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@
<url>https://github.com/Feuermagier/autograder/tree/main</url>
</scm>

<repositories>
<repository>
<id>spoon-snapshot</id>
<name>Maven Repository for Spoon Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
</repositories>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

Expand All @@ -45,7 +53,7 @@

<slf4j.version>2.0.16</slf4j.version>
<jackson.version>2.18.0</jackson.version>
<spoon.version>11.1.0</spoon.version>
<spoon.version>11.1.1-SNAPSHOT</spoon.version>
<fluent.version>0.70</fluent.version>
<reflections.version>0.10.2</reflections.version>

Expand Down

0 comments on commit b613b11

Please sign in to comment.