Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Crash fix for UnionType parameter in Issue #38 #45

Merged
merged 25 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0314236
[UNCOMPLTE] testcase and visitor method update for unionType parameter
Nov 15, 2023
eeec7ed
test runner add
Nov 15, 2023
d52e8b0
spotless apply
Nov 15, 2023
70e3b75
adding missing imports.
Nov 15, 2023
81d45c8
renaming test classes
Nov 15, 2023
ab2a7f9
handle unsolvable unionType parameter
Nov 17, 2023
a077029
adding test cases
Nov 21, 2023
7fec587
removing unsolved test
Nov 21, 2023
660071e
Merge branch 'main' into issue_38
kelloggm Nov 27, 2023
67c2d61
[UNCOMPLTE] testcase and visitor method update for unionType parameter
Nov 15, 2023
a2daa8c
adding missing imports.
Nov 15, 2023
02a8623
issue fixes in targetvisitor for parameter of union type
Nov 28, 2023
1768703
test fail fix
Nov 28, 2023
75a3b48
adding custom method in custom exception
Nov 28, 2023
8a10d54
using actual type instead of var and removing try catch block where i…
Nov 28, 2023
0c05341
updating constructor of unsolvedClass
Nov 29, 2023
8cbbd49
test case with addition block in catch clauses
Nov 29, 2023
5bfe7e1
Merge remote-tracking branch 'upstream/main' into issue_38
Nov 29, 2023
302cf78
adding testcase for unsolvable exception type
Nov 29, 2023
5e32e2d
checking value presence in optional type
Nov 29, 2023
eefedcb
factoring uniontype resolution to a method, breaking the string build…
Nov 29, 2023
230749f
Merge remote-tracking branch 'upstream/main' into issue_38
Nov 29, 2023
c766008
removing synthetic file mistakenly added in the commit
Nov 29, 2023
8a5bf1f
documenting resolveUnionType method
Nov 29, 2023
34a10d5
add documentation to methods where it is missing
Nov 29, 2023
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 @@ -12,8 +12,11 @@
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.expr.SuperExpr;
import com.github.javaparser.ast.stmt.CatchClause;
import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
import com.github.javaparser.ast.type.ReferenceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.type.UnionType;
import com.github.javaparser.ast.visitor.ModifierVisitor;
import com.github.javaparser.ast.visitor.Visitable;
import com.github.javaparser.resolution.UnsolvedSymbolException;
Expand Down Expand Up @@ -220,19 +223,34 @@ public Visitable visit(MethodDeclaration method, Void p) {
@Override
public Visitable visit(Parameter para, Void p) {
if (insideTargetMethod) {
ResolvedType paraType = para.resolve().getType();
if (paraType.isReferenceType()) {
String paraTypeFullName =
paraType.asReferenceType().getTypeDeclaration().get().getQualifiedName();
usedClass.add(paraTypeFullName);
for (ResolvedType typeParameterValue : paraType.asReferenceType().typeParametersValues()) {
String typeParameterValueName = typeParameterValue.describe();
if (typeParameterValueName.contains("<")) {
// removing the "<...>" part if there is any.
typeParameterValueName =
typeParameterValueName.substring(0, typeParameterValueName.indexOf("<"));
Type type = para.getType();
if (type.isUnionType()) {
resolveUnionType(type.asUnionType());
} else {
// Parameter resolution (para.resolve()) does not work in catch clause.
// However, resolution works on the type of the parameter.
// Bug report: https://github.com/javaparser/javaparser/issues/4240
ResolvedType paramType;
if (para.getParentNode().isPresent() && para.getParentNode().get() instanceof CatchClause) {
tahiat marked this conversation as resolved.
Show resolved Hide resolved
paramType = para.getType().resolve();
} else {
paramType = para.resolve().getType();
}

if (paramType.isReferenceType()) {
String paraTypeFullName =
paramType.asReferenceType().getTypeDeclaration().get().getQualifiedName();
usedClass.add(paraTypeFullName);
for (ResolvedType typeParameterValue :
paramType.asReferenceType().typeParametersValues()) {
String typeParameterValueName = typeParameterValue.describe();
if (typeParameterValueName.contains("<")) {
// removing the "<...>" part if there is any.
typeParameterValueName =
typeParameterValueName.substring(0, typeParameterValueName.indexOf("<"));
}
usedClass.add(typeParameterValueName);
}
usedClass.add(typeParameterValueName);
}
}
}
Expand Down Expand Up @@ -310,4 +328,16 @@ public Visitable visit(NameExpr expr, Void p) {
}
return super.visit(expr, p);
}

/**
* @param type unionType parameter
tahiat marked this conversation as resolved.
Show resolved Hide resolved
*/
private void resolveUnionType(UnionType type) {
for (ReferenceType param : type.getElements()) {
ResolvedType paramType = param.resolve();
String paraTypeFullName =
paramType.asReferenceType().getTypeDeclaration().get().getQualifiedName();
usedClass.add(paraTypeFullName);
}
}
}
22 changes: 21 additions & 1 deletion src/main/java/org/checkerframework/specimin/UnsolvedClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,33 @@ public class UnsolvedClass {
/** This field records the number of type variables for this class */
private int numberOfTypeVariables = 0;

/** This field records if the class is a custom exception */
private boolean isExceptionType = false;

/**
* Create an instance of UnsolvedClass
*
* @param className the name of the class
* @param packageName the name of the package
*/
public UnsolvedClass(@ClassGetSimpleName String className, String packageName) {
this(className, packageName, false);
}

/**
* Create an instance of UnsolvedClass
*
* @param className the name of the class
* @param packageName the name of the package
* @param isException does the class represents an exception?
*/
public UnsolvedClass(
@ClassGetSimpleName String className, String packageName, boolean isException) {
this.className = className;
this.methods = new LinkedHashSet<>();
this.packageName = packageName;
this.classFields = new LinkedHashSet<>();
this.isExceptionType = isException;
}

/**
Expand Down Expand Up @@ -172,7 +188,11 @@ public void updateFieldByType(String currentType, String correctType) {
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("package ").append(packageName).append(";\n");
sb.append("public class ").append(className).append(getTypeVariablesAsString()).append(" {\n");
sb.append("public class ").append(className).append(getTypeVariablesAsString());
if (isExceptionType) {
sb.append(" extends Exception");
}
sb.append(" {\n");
for (String variableDeclarations : classFields) {
sb.append(" " + "public " + variableDeclarations + ";\n");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.github.javaparser.ast.type.ReferenceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.type.TypeParameter;
import com.github.javaparser.ast.type.UnionType;
import com.github.javaparser.ast.visitor.ModifierVisitor;
import com.github.javaparser.ast.visitor.Visitable;
import com.github.javaparser.resolution.UnsolvedSymbolException;
Expand All @@ -57,6 +58,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.signature.qual.ClassGetSimpleName;
import org.checkerframework.checker.signature.qual.DotSeparatedIdentifiers;
Expand Down Expand Up @@ -526,7 +528,7 @@ public Visitable visit(MethodDeclaration node, Void arg) {
try {
nodeType.resolve();
} catch (UnsolvedSymbolException | UnsupportedOperationException e) {
updateUnsolvedClassWithClassName(nodeTypeSimpleForm);
updateUnsolvedClassWithClassName(nodeTypeSimpleForm, false);
}
}

Expand Down Expand Up @@ -655,24 +657,22 @@ public Visitable visit(ClassOrInterfaceType typeExpr, Void p) {
@Override
public Visitable visit(Parameter parameter, Void p) {
try {
parameter.resolve();
if (parameter.getType() instanceof UnionType) {
resolveUnionType(parameter);
} else {
if (parameter.getParentNode().isPresent()
&& parameter.getParentNode().get() instanceof CatchClause) {
parameter.getType().resolve();
} else {
parameter.resolve();
}
}
return super.visit(parameter, p);
}
// If the parameter originates from a Java built-in library, such as java.io or java.lang,
// an UnsupportedOperationException will be thrown instead.
catch (UnsolvedSymbolException | UnsupportedOperationException e) {
String parameterInString = parameter.toString();
if (isAClassPath(parameterInString)) {
// parameterInString needs to be a fully-qualified name. As this parameter has a form of
// class path, we can say that it is a fully-qualified name
@SuppressWarnings("signature")
UnsolvedClass newClass = getSimpleSyntheticClassFromFullyQualifiedName(parameterInString);
updateMissingClass(newClass);
} else {
// since it is unsolved, it could not be a primitive type
@ClassGetSimpleName String className = parameter.getType().asClassOrInterfaceType().getName().asString();
updateUnsolvedClassWithClassName(className);
}
handleParameterResolveFailure(parameter);
}
gotException = true;
return super.visit(parameter, p);
Expand All @@ -695,7 +695,7 @@ public Visitable visit(ObjectCreationExpr newExpr, Void p) {
try {
List<String> argumentsCreation = getArgumentsFromObjectCreation(newExpr);
UnsolvedMethod creationMethod = new UnsolvedMethod("", type, argumentsCreation);
updateUnsolvedClassWithClassName(type, creationMethod);
updateUnsolvedClassWithClassName(type, false, creationMethod);
} catch (Exception q) {
// can not solve the parameters for this object creation in this current run
}
Expand All @@ -705,6 +705,46 @@ public Visitable visit(ObjectCreationExpr newExpr, Void p) {
return newExpr;
}

/**
* @param parameter parameter from visitor method which is unsolvable.
*/
private void handleParameterResolveFailure(@NonNull Parameter parameter) {
String parameterInString = parameter.toString();
if (isAClassPath(parameterInString)) {
// parameterInString needs to be a fully-qualified name. As this parameter has a form of
// class path, we can say that it is a fully-qualified name
@SuppressWarnings("signature")
UnsolvedClass newClass = getSimpleSyntheticClassFromFullyQualifiedName(parameterInString);
updateMissingClass(newClass);
} else {
// since it is unsolved, it could not be a primitive type
@ClassGetSimpleName String className = parameter.getType().asClassOrInterfaceType().getName().asString();
if (parameter.getParentNode().isPresent()
&& parameter.getParentNode().get() instanceof CatchClause) {
updateUnsolvedClassWithClassName(className, true);
} else {
updateUnsolvedClassWithClassName(className, false);
}
}
}

/**
* Given the unionType parameter, this method will try resolving each element separately. If any
* of the element is unsolvable, an unsolved class instance will be created to generate synthetic
* class for the element.
*
* @param parameter unionType parameter from visitor class
*/
private void resolveUnionType(@NonNull Parameter parameter) {
for (var param : parameter.getType().asUnionType().getElements()) {
try {
param.resolve();
} catch (UnsolvedSymbolException | UnsupportedOperationException e) {
handleParameterResolveFailure(parameter);
}
}
}

/**
* Given the variable type and the basic declaration of that variable (such as "int x", "boolean
* y", "Car redTruck",...), this methods will add an initial value to that declaration of the
Expand Down Expand Up @@ -790,7 +830,7 @@ public void updateUnsolvedClassWithMethod(
returnType = desiredReturnType;
}
UnsolvedMethod thisMethod = new UnsolvedMethod(methodName, returnType, listOfParameters);
UnsolvedClass missingClass = updateUnsolvedClassWithClassName(className, thisMethod);
UnsolvedClass missingClass = updateUnsolvedClassWithClassName(className, false, thisMethod);
syntheticMethodAndClass.put(methodName, missingClass);

// if the return type is not specified, a synthetic return type will be created. This part of
Expand Down Expand Up @@ -873,15 +913,18 @@ public void updateClassesFromJarSourcesForMethodCall(MethodCallExpr expr) {
* @param nameOfClass the name of an unsolved class
* @param unsolvedMethods unsolved methods to add to the class before updating this visitor's set
* missing classes (optional, may be omitted)
* @param isExceptionType if the class is of exceptionType
* @return the newly-created UnsolvedClass method, for further processing. This output may be
* ignored.
*/
public UnsolvedClass updateUnsolvedClassWithClassName(
@ClassGetSimpleName String nameOfClass, UnsolvedMethod... unsolvedMethods) {
@ClassGetSimpleName String nameOfClass,
boolean isExceptionType,
UnsolvedMethod... unsolvedMethods) {
// if the name of the class is not present among import statements, we assume that this unsolved
// class is in the same directory as the current class
String packageName = classAndPackageMap.getOrDefault(nameOfClass, currentPackage);
UnsolvedClass result = new UnsolvedClass(nameOfClass, packageName);
UnsolvedClass result = new UnsolvedClass(nameOfClass, packageName, isExceptionType);
for (UnsolvedMethod unsolvedMethod : unsolvedMethods) {
result.addMethod(unsolvedMethod);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.checkerframework.specimin;

import java.io.IOException;
import org.junit.Test;

public class CustomExceptionTest {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"customexception",
new String[] {"com/example/Simple.java"},
new String[] {"com.example.Simple#test()"});
}
}
15 changes: 15 additions & 0 deletions src/test/java/org/checkerframework/specimin/Issue38Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.checkerframework.specimin;
kelloggm marked this conversation as resolved.
Show resolved Hide resolved

import java.io.IOException;
import org.junit.Test;

/** This test checks if Specimin will work for Union types */
public class Issue38Test {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"issue38",
new String[] {"com/example/Issue38.java"},
new String[] {"com.example.Issue38#test()"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.checkerframework.specimin;

import java.io.IOException;
import org.junit.Test;

public class NestedCatchClauseTest {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"nestedcatchclause",
new String[] {"com/example/Simple.java"},
new String[] {"com.example.Simple#test()"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.checkerframework.specimin;

import java.io.IOException;
import org.junit.Test;

public class UnsolvableExceptionTest {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"unsolvedexception",
new String[] {"com/example/Simple.java"},
new String[] {"com.example.Simple#test()"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example;

public class CustomException extends Exception {

public CustomException(String msg) {
throw new Error();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example;

public class Simple {

public void test() {
try {
throw new CustomException("dummy");
} catch (CustomException e) {
e.printStackTrace();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example;
public class CustomException extends Exception {
public CustomException (String msg) {
}
}
14 changes: 14 additions & 0 deletions src/test/resources/customexception/input/com/example/Simple.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example;

import org.checkerframework.specimin.CustomExceptionTest;

public class Simple {

public void test() {
try {
throw new CustomException("dummy");
} catch (CustomException e) {
e.printStackTrace();
}
}
}
16 changes: 16 additions & 0 deletions src/test/resources/issue38/expected/com/example/Issue38.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example;

public class Issue38 {

static class ExampleClass {
}

public void test() {
try {
Class<?> exampleClass = ExampleClass.class;
ExampleClass instance = (ExampleClass) exampleClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
Loading