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

Remove unused imports #354

Merged
merged 19 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from 17 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 @@ -28,8 +28,7 @@
*/
public class AnnotationParameterTypesVisitor extends SpeciminStateVisitor {
/**
* Constructs a new SolveMethodOverridingVisitor with the provided sets of target methods, used
* members, and used classes.
* Constructs a new AnnotationParameterTypesVisitor with the previous visitor
*
* @param previousVisitor the last visitor to run before this one
*/
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/org/checkerframework/specimin/JavaParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,32 @@ public static void removeNode(Node node) {
}
}

/**
* This method checks if a string has the form of a class path.
*
* @param potentialClassPath the string to be checked
* @return true if the string is a class path
*/
public static boolean isAClassPath(String potentialClassPath) {
List<String> elements = Splitter.onPattern("\\.").splitToList(potentialClassPath);
int elementsCount = elements.size();
return elementsCount > 1
&& isCapital(elements.get(elementsCount - 1))
// Classpaths cannot contain spaces!
&& elements.stream().noneMatch(s -> s.contains(" "));
}

/**
* This method checks if a string is capitalized
*
* @param string the string to be checked
* @return true if the string is capitalized
*/
public static boolean isCapital(String string) {
Character first = string.charAt(0);
return Character.isUpperCase(first);
}

/**
* Utility method to check if the given declaration is a local class declaration.
*
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/org/checkerframework/specimin/SpeciminRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ private static void performMinimizationImpl(
cu.accept(methodPruner, null);
}

removeUnusedImports(parsedTargetFiles);

// cache to avoid called Files.createDirectories repeatedly with the same arguments
Set<Path> createdDirectories = new HashSet<>();
Set<String> targetFilesAbsolutePaths = new HashSet<>();
Expand Down Expand Up @@ -647,6 +649,20 @@ private static SpeciminStateVisitor processAnnotationTypes(
return annotationParameterTypesVisitor;
}

/**
* Removes all unused imports in each output file through {@code UnusedImportRemoverVisitor}.
*
* @param parsedTargetFiles the files to remove unused imports
*/
private static void removeUnusedImports(Map<String, CompilationUnit> parsedTargetFiles) {
UnusedImportRemoverVisitor unusedImportRemover = new UnusedImportRemoverVisitor();

for (CompilationUnit cu : parsedTargetFiles.values()) {
cu.accept(unusedImportRemover, null);
unusedImportRemover.removeUnusedImports();
}
}

/**
* Helper method to create a human-readable table of the unfound members and each member in the
* same class that was considered.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ public Visitable visit(MethodCallExpr call, Void p) {
// a call to a fully-qualified static method) or the scope is a simple name.
// In the simple name case, append the current package to the front, since
// if it had been imported we wouldn't be in this situation.
if (UnsolvedSymbolVisitor.isAClassPath(scopeAsString)) {
if (JavaParserUtil.isAClassPath(scopeAsString)) {
resolvedYetStuckMethodCall.add(scopeAsString + "." + call.getNameAsString());
usedTypeElements.add(scopeAsString);
} else {
Expand Down Expand Up @@ -869,7 +869,7 @@ public static void updateUsedClassWithQualifiedClassName(

String potentialOuterClass =
qualifiedClassName.substring(0, qualifiedClassName.lastIndexOf("."));
if (UnsolvedSymbolVisitor.isAClassPath(potentialOuterClass)) {
if (JavaParserUtil.isAClassPath(potentialOuterClass)) {
updateUsedClassWithQualifiedClassName(
potentialOuterClass, usedTypeElement, nonPrimaryClassesToPrimaryClass);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public void processAnnotations(AnnotationExpr annotation) {
isResolved = false;
}

if (!UnsolvedSymbolVisitor.isAClassPath(annotationName)) {
if (!JavaParserUtil.isAClassPath(annotationName)) {
if (!classToFullClassName.containsKey(annotationName)) {
// An annotation not imported and from the java.lang package is not our concern.
if (!JavaLangUtils.isJavaLangName(annotationName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ public boolean extend(String targetTypeName, String extendsName, UnsolvedSymbolV
|| "java.lang.annotation.Annotation".equals(extendsName)) {
setIsAnAnnotationToTrue();
} else {
if (!UnsolvedSymbolVisitor.isAClassPath(extendsName)) {
if (!JavaParserUtil.isAClassPath(extendsName)) {
extendsName = visitor.getPackageFromClassName(extendsName) + "." + extendsName;
}
extend(extendsName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1086,7 +1086,7 @@ public Visitable visit(FieldAccessExpr node, Void p) {
} catch (UnsolvedSymbolException | UnsupportedOperationException e) {
// for a qualified name field access such as org.sample.MyClass.field, org.sample will also be
// considered FieldAccessExpr.
if (isAClassPath(node.getScope().toString())) {
if (JavaParserUtil.isAClassPath(node.getScope().toString())) {
gotException();
}
}
Expand All @@ -1101,7 +1101,7 @@ public Visitable visit(MethodReferenceExpr node, Void p) {
if (scope.isTypeExpr()) {
Type scopeAsType = scope.asTypeExpr().getType();
String scopeAsTypeFQN = scopeAsType.asString();
if (!isAClassPath(scopeAsTypeFQN) && scopeAsType.isClassOrInterfaceType()) {
if (!JavaParserUtil.isAClassPath(scopeAsTypeFQN) && scopeAsType.isClassOrInterfaceType()) {
scopeAsTypeFQN =
getQualifiedNameForClassOrInterfaceType(scopeAsType.asClassOrInterfaceType());
}
Expand Down Expand Up @@ -1219,7 +1219,7 @@ public Visitable visit(ClassOrInterfaceType typeExpr, Void p) {
// like com.example.Dog dog, JavaParser considers its package components (com and com.example)
// as types, too. This issue happens even when the source file of the Dog class is present in
// the codebase.
if (!isCapital(typeExpr.getName().asString())) {
if (!JavaParserUtil.isCapital(typeExpr.getName().asString())) {
return super.visit(typeExpr, p);
}
// type belonging to a class declaration will be handled by the visit method for
Expand Down Expand Up @@ -1416,7 +1416,7 @@ Test foo() {

UnsolvedClassOrInterface unsolvedAnnotation;

if (isAClassPath(anno.getNameAsString())) {
if (JavaParserUtil.isAClassPath(anno.getNameAsString())) {
@SuppressWarnings("signature") // Already guaranteed to be a FQN here
@FullyQualifiedName String qualifiedTypeName = anno.getNameAsString();
unsolvedAnnotation = getSimpleSyntheticClassFromFullyQualifiedName(qualifiedTypeName);
Expand Down Expand Up @@ -1463,7 +1463,7 @@ Test foo() {

UnsolvedClassOrInterface unsolvedAnnotation;

if (isAClassPath(anno.getNameAsString())) {
if (JavaParserUtil.isAClassPath(anno.getNameAsString())) {
@SuppressWarnings("signature") // Already guaranteed to be a FQN here
@FullyQualifiedName String qualifiedTypeName = anno.getNameAsString();
unsolvedAnnotation = getSimpleSyntheticClassFromFullyQualifiedName(qualifiedTypeName);
Expand Down Expand Up @@ -1520,7 +1520,7 @@ Test foo() {

UnsolvedClassOrInterface unsolvedAnnotation;

if (isAClassPath(anno.getNameAsString())) {
if (JavaParserUtil.isAClassPath(anno.getNameAsString())) {
@SuppressWarnings("signature") // Already guaranteed to be a FQN here
@FullyQualifiedName String qualifiedTypeName = anno.getNameAsString();
unsolvedAnnotation = getSimpleSyntheticClassFromFullyQualifiedName(qualifiedTypeName);
Expand Down Expand Up @@ -1712,12 +1712,13 @@ private void solveSymbolsForClassOrInterfaceType(
}

String packageName, className;
if (isAClassPath(typeRawName)) {
if (JavaParserUtil.isAClassPath(typeRawName)) {
// Two cases: this could be either an Outer.Inner pair or it could
// be a fully-qualified name. If it's an Outer.Inner pair, we identify
// that via the heuristic that there are only two elements if we split on
// the dot and that the whole string is capital
if (typeRawName.indexOf('.') == typeRawName.lastIndexOf('.') && isCapital(typeRawName)) {
if (typeRawName.indexOf('.') == typeRawName.lastIndexOf('.')
&& JavaParserUtil.isCapital(typeRawName)) {
className = typeRawName;
packageName = getPackageFromClassName(typeRawName.substring(0, typeRawName.indexOf('.')));
} else {
Expand Down Expand Up @@ -2529,7 +2530,7 @@ public String getQualifiedNameForClassOrInterfaceType(ClassOrInterfaceType type)
if (splitType.size() > 2) {
// if the above conditions are met, this type is probably already in the qualified form.
return typeAsString;
} else if (isCapital(typeAsString)) {
} else if (JavaParserUtil.isCapital(typeAsString)) {
// Heuristic: if the type name has two dot-separated components and
// the first one is capitalized, then it's probably an inner class.
// Return the outer class' package.
Expand Down Expand Up @@ -2911,7 +2912,7 @@ public void updateMissingClass(UnsolvedClassOrInterface missedClass) {
// name might be "java.util" and the class name might be "Map.Entry".
String outerClassName = null, innerClassName = null;
// First case, looking for "Map.Entry" pattern
if (isCapital(qualifiedName)
if (JavaParserUtil.isCapital(qualifiedName)
&&
// This test checks that it has only one .
qualifiedName.indexOf('.') == qualifiedName.lastIndexOf('.')) {
Expand Down Expand Up @@ -3073,32 +3074,6 @@ public static String toCapital(String string) {
return Ascii.toUpperCase(string.substring(0, 1)) + string.substring(1);
}

/**
* This method checks if a string is capitalized
*
* @param string the string to be checked
* @return true if the string is capitalized
*/
public static boolean isCapital(String string) {
Character first = string.charAt(0);
return Character.isUpperCase(first);
}

/**
* This method checks if a string has the form of a class path.
*
* @param potentialClassPath the string to be checked
* @return true if the string is a class path
*/
public static boolean isAClassPath(String potentialClassPath) {
List<String> elements = Splitter.onPattern("\\.").splitToList(potentialClassPath);
int elementsCount = elements.size();
return elementsCount > 1
&& isCapital(elements.get(elementsCount - 1))
// Classpaths cannot contain spaces!
&& elements.stream().noneMatch(s -> s.contains(" "));
}

/**
* Given the name of a class in the @FullyQualifiedName, this method will create a synthetic class
* for that class
Expand All @@ -3108,9 +3083,9 @@ && isCapital(elements.get(elementsCount - 1))
*/
public static UnsolvedClassOrInterface getSimpleSyntheticClassFromFullyQualifiedName(
@FullyQualifiedName String fullyName) {
if (!isAClassPath(fullyName)) {
if (!JavaParserUtil.isAClassPath(fullyName)) {
throw new RuntimeException(
"Check with isAClassPath first before using"
"Check with JavaParserUtil.isAClassPath first before using"
+ " getSimpleSyntheticClassFromFullyQualifiedName. Non-classpath-like name: "
+ fullyName);
}
Expand Down Expand Up @@ -3212,7 +3187,7 @@ public boolean isAnUnsolvedStaticMethodCalledByAQualifiedClassName(MethodCallExp
return false;
}
String callerToString = callerExpression.get().toString();
return isAClassPath(callerToString);
return JavaParserUtil.isAClassPath(callerToString);
}
}

Expand All @@ -3227,7 +3202,7 @@ public boolean isAnUnsolvedStaticMethodCalledByAQualifiedClassName(MethodCallExp
*/
public boolean isAQualifiedFieldSignature(String field) {
String caller = field.substring(0, field.lastIndexOf("."));
return isAClassPath(caller);
return JavaParserUtil.isAClassPath(caller);
}

/**
Expand Down Expand Up @@ -3655,7 +3630,7 @@ private void lookupTypeArgumentFQN(StringBuilder fullyQualifiedName, Type typeAr
fullyQualifiedName.append("? extends ");
lookupTypeArgumentFQN(fullyQualifiedName, asWildcardType.getExtendedType().get());
}
} else if (isAClassPath(erased)) {
} else if (JavaParserUtil.isAClassPath(erased)) {
// If it's already a fully qualified name, don't do anything
fullyQualifiedName.append(typeArgument.asString());
} else {
Expand Down
Loading
Loading