Skip to content
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ target/
# Any pictures and dot files
graphics/

# Folder I use for output of manual tests
# Folder for output of manual tests
manual_test_results/

.DS_Store
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<groupId>de.jsilbereisen</groupId>
<artifactId>perfumator-java</artifactId>
<version>0.4.1</version>
<version>0.4.2</version>
<packaging>jar</packaging>

<developers>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
import com.github.javaparser.resolution.model.typesystem.ReferenceTypeImpl;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.model.DetectedInstance;
import de.jsilbereisen.perfumator.model.perfume.Perfume;
import de.jsilbereisen.perfumator.util.NodeUtil;
import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -29,16 +27,15 @@ public class AssertAllDetector implements Detector<Perfume> {

private JavaParserFacade analysisContext;

private static final String QUALIFIED_ASSERT_ALL_METHOD_NAME = "org.junit.jupiter.api.Assertions.assertAll";
private static final String ASSERTIONS_IMPORT_NAME = "org.junit.jupiter.api.Assertions";
private static final String DECLARING_CLASS = "org.junit.jupiter.api.Assertions";
private static final String ASSERT_ALL = "assertAll";

@Override
public @NotNull List<DetectedInstance<Perfume>> detect(@NotNull CompilationUnit astRoot) {
List<DetectedInstance<Perfume>> detectedInstances = new ArrayList<>();
List<MethodCallExpr> assertAllMethodCallExpressions = getAssertAllMethodCalls(astRoot);
assertAllMethodCallExpressions
.forEach(callExpr -> detectedInstances.add(DetectedInstance.from(callExpr, perfume, astRoot)));
assertAllMethodCallExpressions.forEach(callExpr ->
detectedInstances.add(DetectedInstance.from(callExpr, perfume, astRoot)));
return detectedInstances;
}

Expand All @@ -53,30 +50,18 @@ public void setAnalysisContext(@Nullable JavaParserFacade analysisContext) {
}

private List<MethodCallExpr> getAssertAllMethodCalls(@NotNull CompilationUnit astRoot) {
return astRoot.findAll(MethodCallExpr.class, expr -> {
// contains instead of equals because of possible 'Assertions.assertAll' calls
if (!expr.getNameAsString().contains(ASSERT_ALL)) {
return false;
}
if (expr.getScope().isPresent()) {
// for non-static imports
ResolvedType resolvedType;
try {
resolvedType = expr.getScope().get().calculateResolvedType();
} catch (Exception e) {
System.out.println(expr.getNameAsString());
System.out.println(e.getMessage());
return false;
}
return resolvedType instanceof ReferenceTypeImpl referenceType
&& referenceType.getQualifiedName().equals(ASSERTIONS_IMPORT_NAME);
} else {
// for static imports
Optional<ResolvedMethodDeclaration> resolvedMethodDeclaration
= NodeUtil.resolveSafely(expr, this, expr.getNameAsString());
return resolvedMethodDeclaration.map(methodDeclaration -> methodDeclaration.getQualifiedName().equals(QUALIFIED_ASSERT_ALL_METHOD_NAME))
.orElse(false);
}
});
return astRoot.findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getNameAsString().equals(ASSERT_ALL))
.filter(expr -> {
ResolvedMethodDeclaration methodDeclaration;
try {
methodDeclaration = analysisContext.solve(expr).getCorrespondingDeclaration();
} catch (UnsupportedOperationException e) {
e.printStackTrace();
return false;
}
ResolvedReferenceTypeDeclaration referenceType = methodDeclaration.declaringType().asReferenceType();
return DECLARING_CLASS.equals(referenceType.getQualifiedName());
}).toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.resolution.model.typesystem.ReferenceTypeImpl;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.model.DetectedInstance;
Expand All @@ -16,7 +15,7 @@

/**
* {@link Detector} for the "JFrame dispose" {@link Perfume}.
* Detects the perfume only if the method is part of the {@link javax.swing.JFrame} class.
* Detects the perfume only if the method is part of the {@link java.awt.Window} class.
*/
public class JFrameDisposeDetector implements Detector<Perfume> {

Expand All @@ -25,14 +24,14 @@ public class JFrameDisposeDetector implements Detector<Perfume> {
private JavaParserFacade analysisContext;

private static final String DISPOSE_METHOD_NAME = "dispose";
private static final String QUALIFIED_JFRAME_CLASS_NAME = "javax.swing.JFrame";
private static final String DECLARING_CLASS = "java.awt.Window";

@Override
public @NotNull List<DetectedInstance<Perfume>> detect(@NotNull CompilationUnit astRoot) {
List<DetectedInstance<Perfume>> detectedInstances = new ArrayList<>();
List<MethodCallExpr> disposeMethodCallExpressions = getJFrameDisposeMethodCalls(astRoot);
disposeMethodCallExpressions
.forEach(expr -> detectedInstances.add(DetectedInstance.from(expr, perfume, astRoot)));
disposeMethodCallExpressions.forEach(expr ->
detectedInstances.add(DetectedInstance.from(expr, perfume, astRoot)));
return detectedInstances;
}

Expand All @@ -47,25 +46,26 @@ public void setAnalysisContext(@Nullable JavaParserFacade analysisContext) {
}

private List<MethodCallExpr> getJFrameDisposeMethodCalls(@NotNull CompilationUnit astRoot) {
return astRoot.findAll(MethodCallExpr.class, expr -> {
if (!expr.getNameAsString().equals(DISPOSE_METHOD_NAME)) {
return false;
}
var scope = expr.getScope();
if (scope.isPresent()) {
ResolvedType resolvedType;
try {
resolvedType = scope.get().calculateResolvedType();
} catch (Exception e) {
System.out.println(expr.getNameAsString());
System.out.println(e.getMessage());
return false;
}
if (resolvedType instanceof ReferenceTypeImpl referenceType) {
return referenceType.getQualifiedName().equals(QUALIFIED_JFRAME_CLASS_NAME);
}
}
return false;
});
return astRoot.findAll(MethodCallExpr.class).stream()
// only consider methods with name "dispose"
.filter(expr -> expr.getNameAsString().equals(DISPOSE_METHOD_NAME))
// ensure that the declaring class is JFrame
.filter(expr -> {
ResolvedMethodDeclaration disposeDeclaration;
try {
disposeDeclaration = analysisContext.solve(expr).getCorrespondingDeclaration();
} catch (UnsupportedOperationException e) {
e.printStackTrace();
return false;
}
var referenceType = disposeDeclaration.declaringType().asReferenceType();
if (referenceType.getQualifiedName().equals(DECLARING_CLASS)) {
return true;
} else {
return referenceType.getAllAncestors().stream()
.anyMatch(ancestor ->
ancestor.getQualifiedName().equals(DECLARING_CLASS));
}
}).toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedAnnotationDeclaration;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.model.DetectedInstance;
import de.jsilbereisen.perfumator.model.perfume.Perfume;
import de.jsilbereisen.perfumator.util.NodeUtil;
import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -23,16 +23,16 @@ public class ParameterizedTestDetector implements Detector<Perfume> {
private Perfume perfume;

private JavaParserFacade analysisContext;
private static final String PARAMETERIZED_TEST_PACKAGE = "org.junit.jupiter.params.";

private static final String QUALIFIED_NAME = "org.junit.jupiter.params.ParameterizedTest";
private static final String PARAMETERIZED_TEST_IDENTIFIER = "ParameterizedTest";

@Override
public @NotNull List<DetectedInstance<Perfume>> detect(@NotNull CompilationUnit astRoot) {
List<DetectedInstance<Perfume>> detectedInstances = new ArrayList<>();
List<MethodDeclaration> parameterizedTestMethodDeclarations = getParameterizedTestMethodDeclarations(astRoot);
parameterizedTestMethodDeclarations
.forEach(declaration -> detectedInstances.add(DetectedInstance.from(declaration, perfume, astRoot)));
parameterizedTestMethodDeclarations.forEach(declaration
-> detectedInstances.add(DetectedInstance.from(declaration, perfume, astRoot)));
return detectedInstances;
}

Expand All @@ -47,13 +47,20 @@ public void setAnalysisContext(@Nullable JavaParserFacade analysisContext) {
}

private List<MethodDeclaration> getParameterizedTestMethodDeclarations(@NotNull CompilationUnit astRoot) {
return astRoot.findAll(MethodDeclaration.class, methodDeclaration -> methodDeclaration.getAnnotations().stream()
// filter out annotations that do not contain 'ParameterizedTest'
.filter(annotation -> annotation.getNameAsString().contains(PARAMETERIZED_TEST_IDENTIFIER))
// try to resolve the symbol in order to get the declaration
.map(paramTestAnnotation -> NodeUtil.resolveSafely(paramTestAnnotation, this, paramTestAnnotation.getNameAsString()))
.filter(Optional::isPresent)
.map(resolvedAnnotationDeclaration -> resolvedAnnotationDeclaration.get().getQualifiedName())
.anyMatch(qualifiedName -> qualifiedName.equals(PARAMETERIZED_TEST_PACKAGE + PARAMETERIZED_TEST_IDENTIFIER)));
return astRoot.findAll(MethodDeclaration.class, expr -> {
var annotations = expr.getAnnotations();
annotations.removeIf(annotation ->
!annotation.getNameAsString().contains(PARAMETERIZED_TEST_IDENTIFIER));
return annotations.stream().anyMatch(annotation -> {
ResolvedAnnotationDeclaration resolvedAnnotationDeclaration;
try {
resolvedAnnotationDeclaration = analysisContext.solve(annotation).getCorrespondingDeclaration();
} catch (UnsupportedOperationException e) {
e.printStackTrace();
return false;
}
return QUALIFIED_NAME.equals(resolvedAnnotationDeclaration.getQualifiedName());
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedAnnotationDeclaration;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.model.DetectedInstance;
import de.jsilbereisen.perfumator.model.perfume.Perfume;
import de.jsilbereisen.perfumator.util.NodeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -56,13 +56,24 @@ private Set<String> getQualifiedAnnotations() {
}

private List<MethodDeclaration> getSetupAndTeardownMethodDeclarations(@NotNull CompilationUnit astRoot) {
return astRoot.findAll(MethodDeclaration.class, methodDeclaration -> methodDeclaration.getAnnotations().stream()
// filter out annotations that do not contain any of the four relevant annotations
.filter(annotation -> TEST_ANNOTATIONS.stream().anyMatch(testAnnotation -> annotation.getNameAsString().contains(testAnnotation)))
// try to resolve the symbol in order to get the declaration
.map(testAnnotation -> NodeUtil.resolveSafely(testAnnotation, this, testAnnotation.getNameAsString()))
.filter(Optional::isPresent)
.map(resolvedAnnotationDeclaration -> resolvedAnnotationDeclaration.get().getQualifiedName())
.anyMatch(qualifiedName -> getQualifiedAnnotations().contains(qualifiedName)));
return astRoot.findAll(MethodDeclaration.class, expr -> {
var annotations = expr.getAnnotations();
// remove annotation if it does not contain any of the four annotations in TEST_ANNOTATIONS
annotations.removeIf(annotation ->
TEST_ANNOTATIONS.stream().noneMatch(allowedAnnotation -> annotation.getNameAsString().contains(allowedAnnotation)));
return annotations.stream().anyMatch(annotation -> {
ResolvedAnnotationDeclaration resolvedAnnotationDeclaration;
try {
resolvedAnnotationDeclaration = analysisContext.solve(annotation).getCorrespondingDeclaration();
} catch (UnsupportedOperationException e) {
e.printStackTrace();
return false;
}
Set<String> fullyQualifiedAnnotations = TEST_ANNOTATIONS.stream().map(allowedAnnotation ->
IMPORT_QUALIFIER + allowedAnnotation).collect(Collectors.toSet());
return fullyQualifiedAnnotations.stream().anyMatch(qualifiedAnnotation ->
resolvedAnnotationDeclaration.getQualifiedName().equals(qualifiedAnnotation));
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,11 @@ private List<ObjectCreationExpr> getNewTimerExpressions(@NotNull CompilationUnit
try {
resolvedType = expr.calculateResolvedType();
} catch (Exception e) {
System.out.println(expr);
System.out.println(e.getMessage());
e.printStackTrace();
return false;
}
return resolvedType instanceof ReferenceTypeImpl referenceType
&& referenceType.getQualifiedName().equals(QUALIFIED_TIMER_NAME);
&& QUALIFIED_TIMER_NAME.equals(referenceType.getQualifiedName());
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
import com.github.javaparser.resolution.model.typesystem.ReferenceTypeImpl;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.model.DetectedInstance;
Expand All @@ -29,9 +28,7 @@ public class ThreadSafeSwingDetector implements Detector<Perfume> {

private final static String INVOKE_LATER = "invokeLater";
private final static String INVOKE_AND_WAIT = "invokeAndWait";
private final static Set<String> QUALIFIED_METHOD_NAMES
= Set.of("javax.swing.SwingUtilities.invokeLater", "javax.swing.SwingUtilities.invokeAndWait");
private final static String IMPORT = "javax.swing.SwingUtilities";
private final static String DECLARING_CLASS = "javax.swing.SwingUtilities";

@Override
public @NotNull List<DetectedInstance<Perfume>> detect(@NotNull CompilationUnit astRoot) {
Expand All @@ -52,29 +49,18 @@ public void setAnalysisContext(@Nullable JavaParserFacade analysisContext) {
}

private List<MethodCallExpr> getInvokeLaterInvokeAndWaitMethodCalls(@NotNull CompilationUnit astRoot) {
return astRoot.findAll(MethodCallExpr.class, expr -> {
// contains instead of equals because of possible 'SwingUtilities.invokeLater' and '-.invokeAndWait' calls
if (!expr.getNameAsString().contains(INVOKE_LATER) && !expr.getNameAsString().contains(INVOKE_AND_WAIT)) {
return false;
}
if (expr.getScope().isPresent()) {
// for non-static imports
ResolvedType resolvedType;
try {
resolvedType = expr.getScope().get().calculateResolvedType();
} catch (Exception e) {
System.out.println(expr.getNameAsString());
System.out.println(e.getMessage());
return false;
}
return resolvedType instanceof ReferenceTypeImpl referenceType
&& referenceType.getQualifiedName().equals(IMPORT);
} else {
// for static imports
ResolvedMethodDeclaration resolvedMethodDeclaration = expr.resolve();
String qualifiedName = resolvedMethodDeclaration.getQualifiedName();
return QUALIFIED_METHOD_NAMES.contains(qualifiedName);
}
});
return astRoot.findAll(MethodCallExpr.class).stream()
.filter(expr -> Set.of(INVOKE_AND_WAIT, INVOKE_LATER).contains(expr.getNameAsString()))
.filter(expr -> {
ResolvedMethodDeclaration methodDeclaration;
try {
methodDeclaration = analysisContext.solve(expr).getCorrespondingDeclaration();
} catch (UnsupportedOperationException e) {
e.printStackTrace();
return false;
}
ResolvedReferenceTypeDeclaration referenceType = methodDeclaration.declaringType().asReferenceType();
return DECLARING_CLASS.equals(referenceType.getQualifiedName());
}).toList();
}
}
Loading