Skip to content

Commit

Permalink
address CR comments
Browse files Browse the repository at this point in the history
  • Loading branch information
theron-wang committed Aug 9, 2024
1 parent 38a78a9 commit a319a63
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,8 @@ public static String[] getTypesForOp(String binOp) {
}

/**
* Is a type primitive (int, char, boolean, etc.)?
* Is a type primitive (int, char, boolean, etc.)? This method returns false for boxed types
* (Integer, Character, Boolean, etc.)
*
* @param type the type to check
* @return true iff the type is primitive
Expand All @@ -326,12 +327,12 @@ public static boolean isPrimitive(String type) {
}

/**
* Converts a primitive to its object wrapper class (i.e. int --> Integer)
* Converts a primitive to its boxed type (i.e. int --> Integer)
*
* @param primitive the primitive type (int, boolean, char, etc.)
* @return the primitive as an object (int --> Integer, char --> Character)
* @return the boxed type (int --> Integer, char --> Character)
*/
public static String getPrimitiveAsObject(String primitive) {
public static String getPrimitiveAsBoxedType(String primitive) {
String converted = primitivesToObjects.get(primitive);

if (converted == null) {
Expand Down
45 changes: 45 additions & 0 deletions src/main/java/org/checkerframework/specimin/JavaParserUtil.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.checkerframework.specimin;

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.AnnotationDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.ConstructorDeclaration;
Expand All @@ -12,10 +14,13 @@
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.nodeTypes.NodeWithDeclaration;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.resolution.UnsolvedSymbolException;
import com.github.javaparser.resolution.types.ResolvedReferenceType;
import com.github.javaparser.resolution.types.ResolvedType;
import com.google.common.base.Splitter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -200,6 +205,46 @@ public static Node getEnclosingClassLike(Node node) {
return parent;
}

/**
* Given a String of types (separated by commas), return a List of these types, with any
* primitives converted to their object counterparts. Use this instead of {@code .split(", ")} to
* properly handle generics.
*
* @param commaSeparatedTypes A string of comma separated types
* @return a list of strings representing the types in commaSeparatedTypes
*/
public static List<String> getReferenceTypesFromCommaSeparatedString(String commaSeparatedTypes) {
if (commaSeparatedTypes == null || commaSeparatedTypes.isBlank()) {
return Collections.EMPTY_LIST;
}

// Splitting them is simply to change primitives to objects so we do not
// get an error when parsing in StaticJavaParser (note that this array)
// may contain incomplete types like ["Map<String", "Object>"]
String[] tokens = commaSeparatedTypes.split(",");

for (int i = 0; i < tokens.length; i++) {
if (JavaLangUtils.isPrimitive(tokens[i].trim())) {
tokens[i] = JavaLangUtils.getPrimitiveAsBoxedType(tokens[i].trim());
}
}

// Parse as a generic type, then get the type arguments
// This way we can properly differentiate between commas within type arguments
// versus actual commas in javac error messages
Type parsed = StaticJavaParser.parseType("ToParse<" + String.join(", ", tokens) + ">");

List<String> types = new ArrayList<>();
NodeList<Type> typeArguments = parsed.asClassOrInterfaceType().getTypeArguments().orElse(null);

if (typeArguments != null) {
for (Type typeArgument : typeArguments) {
types.add(typeArgument.toString());
}
}
return types;
}

/**
* Returns true iff the innermost enclosing class/interface is an enum.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,12 @@ public boolean replaceParamWithObject(String incorrectTypeName) {
}

/**
* Given a correct method reference type, this method replaces the type at the given index with
* the corrected name
* Corrects the parameter's type at index {@code parameter}
*
* @param parameter The parameter (index) to replace
* @param correctName The type name to replace the parameter as
* @param correctName The type name to replace the parameter type as
*/
public void correctMethodReferenceType(int parameter, String correctName) {
public void correctParameterType(int parameter, String correctName) {
parameterList.set(parameter, correctName);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1634,7 +1634,8 @@ public boolean declaredInCurrentClass(MethodCallExpr method) {
if (!methodDeclared.getName().asString().equals(method.getName().asString())) {
continue;
}
List<String> methodTypesOfArguments = getArgumentTypesFromMethodCall(method, currentPackage);
List<String> methodTypesOfArguments =
getArgumentTypesFromMethodCall(method, currentPackage);
NodeList<Parameter> methodDeclaredParameters = methodDeclared.getParameters();
List<String> methodDeclaredTypesOfParameters = new ArrayList<>();
for (Parameter parameter : methodDeclaredParameters) {
Expand Down Expand Up @@ -2723,8 +2724,8 @@ private boolean isLambdaVoidReturn(LambdaExpr lambda) {
* @param numberOfParams the number of parameters
* @param isVoid true iff the method is void
* @param pkgName the package in which a new functional interface should be created, if necessary
* @return the fully-qualified name of a functional interface that is in-scope and is a supertype
* of the given function, according to javac's arity-based typechecking rules for functions
* @return the fully-qualified name of a functional interface that is in-scope, matches the
* specified arity, and the specified voidness
*/
private String resolveFunctionalInterface(int numberOfParams, boolean isVoid, String pkgName) {
// we need to run at least once more to solve the functional interface we're about to create
Expand Down Expand Up @@ -3738,8 +3739,6 @@ public boolean updateTypeForSyntheticClasses(
* the correct parameter types
* @return true if any method argument was updated
*/
@SuppressWarnings("argument")
// found: int, required: @Interned int
public boolean updateMethodReferenceParameters(Map<String, String> methodReferencesToCorrect) {
boolean updated = false;
for (String methodReference : methodReferencesToCorrect.keySet()) {
Expand All @@ -3757,7 +3756,8 @@ public boolean updateMethodReferenceParameters(Map<String, String> methodReferen

List<String> parameters = new ArrayList<>();

for (String parameter : getReferenceTypesFromCommaSeparatedString(parametersAsString)) {
for (String parameter :
JavaParserUtil.getReferenceTypesFromCommaSeparatedString(parametersAsString)) {
parameters.add(lookupFQNs(parameter.trim()));
}

Expand All @@ -3766,8 +3766,8 @@ public boolean updateMethodReferenceParameters(Map<String, String> methodReferen
resolveFunctionalInterfaceWithFullyQualifiedParameters(
parameters, false, currentPackage);

for (int argument : argumentsToFix) {
method.correctMethodReferenceType(argument, fixed);
for (Integer argument : argumentsToFix) {
method.correctParameterType(argument.intValue(), fixed);
updated = true;
}
}
Expand All @@ -3784,8 +3784,6 @@ public boolean updateMethodReferenceParameters(Map<String, String> methodReferen
* the correct voidness (true if void, false if not)
* @return true if any method was updated
*/
@SuppressWarnings("argument")
// found: int, required: @Interned int
public boolean updateMethodReferenceVoidness(Map<String, Boolean> methodReferencesToCorrect) {
boolean updated = false;
for (String methodReference : methodReferencesToCorrect.keySet()) {
Expand All @@ -3796,13 +3794,14 @@ public boolean updateMethodReferenceVoidness(Map<String, Boolean> methodReferenc
unsolvedMethodsToArguments.entrySet()) {
Set<Integer> argumentsToFix = method.getValue();

for (int argument : argumentsToFix) {
for (Integer argument : argumentsToFix) {
// 2nd phase: Since we've already corrected the parameter types, now
// we should keep them and update voidness
String arg = method.getKey().getParameterList().get(argument);
String arg = method.getKey().getParameterList().get(argument.intValue());

String parametersAsString = arg.substring(arg.indexOf('<') + 1, arg.lastIndexOf('>'));
List<String> parameters = getReferenceTypesFromCommaSeparatedString(parametersAsString);
List<String> parameters =
JavaParserUtil.getReferenceTypesFromCommaSeparatedString(parametersAsString);
// Remove the last element; in updateMethodReferenceParameters we assumed that it was
// non-void
parameters.remove(parameters.size() - 1);
Expand All @@ -3811,7 +3810,7 @@ public boolean updateMethodReferenceVoidness(Map<String, Boolean> methodReferenc
resolveFunctionalInterfaceWithFullyQualifiedParameters(
parameters, methodReferencesToCorrect.get(methodReference), currentPackage);

method.getKey().correctMethodReferenceType(argument, fixed);
method.getKey().correctParameterType(argument.intValue(), fixed);

updated = true;
}
Expand All @@ -3822,46 +3821,6 @@ public boolean updateMethodReferenceVoidness(Map<String, Boolean> methodReferenc
return updated;
}

/**
* Given a String of types (separated by commas), return a List of these types, with any
* primitives converted to their object counterparts. Use this instead of {@code .split(", ")} to
* properly handle generics.
*
* @param commaSeparatedTypes A string of comma separated types
* @return a list of strings representing the types in commaSeparatedTypes
*/
private List<String> getReferenceTypesFromCommaSeparatedString(String commaSeparatedTypes) {
if (commaSeparatedTypes == null || commaSeparatedTypes.isBlank()) {
return new ArrayList<>();
}

// Splitting them is simply to change primitives to objects so we do not
// get an error when parsing in StaticJavaParser (note that this array)
// may contain incomplete types like ["Map<String", "Object>"]
String[] tokens = commaSeparatedTypes.split(",");

for (int i = 0; i < tokens.length; i++) {
if (JavaLangUtils.isPrimitive(tokens[i].trim())) {
tokens[i] = JavaLangUtils.getPrimitiveAsObject(tokens[i].trim());
}
}

// Parse as a generic type, then get the type arguments
// This way we can properly differentiate between commas within type arguments
// versus actual commas in javac error messages
Type parsed = StaticJavaParser.parseType("ToParse<" + String.join(", ", tokens) + ">");

List<String> types = new ArrayList<>();
NodeList<Type> typeArguments = parsed.asClassOrInterfaceType().getTypeArguments().orElse(null);

if (typeArguments != null) {
for (Type typeArgument : typeArguments) {
types.add(typeArgument.toString());
}
}
return types;
}

/**
* Lookup the fully-qualified names of each type in the given string, and replace the simple type
* names in the given string with their fully-qualified equivalents. Return the result.
Expand Down

0 comments on commit a319a63

Please sign in to comment.