From 6f2fc112841b5ce47713a07ddca2dc8e42b0dab1 Mon Sep 17 00:00:00 2001 From: Martin Kellogg Date: Wed, 22 Nov 2023 10:14:27 -0500 Subject: [PATCH 01/14] script that checks that all test oracles are compilable --- .github/workflows/gradle.yml | 2 +- build.gradle | 5 +++++ typecheck_test_outputs.sh | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100755 typecheck_test_outputs.sh diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 16e09cd41..24d229f39 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -24,4 +24,4 @@ jobs: - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Run build with Gradle Wrapper - run: ./gradlew build + run: ./gradlew build expectedTestOutputsMustCompile diff --git a/build.gradle b/build.gradle index 1df1240ae..5d8d5badd 100644 --- a/build.gradle +++ b/build.gradle @@ -47,6 +47,11 @@ task requireJavadoc(type: JavaExec) { args "src/main/java" } +task expectedTestOutputsMustCompile(type: Verification) { + // TODO: should this task run in CI, or as part of the regular tests? + commandLine "sh", "typecheck_test_outputs.sh" +} + checkerFramework { checkers = [ 'org.checkerframework.checker.nullness.NullnessChecker', diff --git a/typecheck_test_outputs.sh b/typecheck_test_outputs.sh new file mode 100755 index 000000000..783f42468 --- /dev/null +++ b/typecheck_test_outputs.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# This script runs javac on all of the expected test outputs under src/test/resources. +# It returns 1 if any of them fail to compile, and zero if all of them do compile. +# It is desirable that all of the expected test outputs compile, because Specimin +# should produce independently-compilable programs. + +returnval=0 + +cd src/test/resources || exit 1 +for testcase in * ; do + cd "${testcase}/expected/" || exit 1 + javaccmd="javac -proc:only -nowarn $(find . -name "*.java")" + # javac relies on word splitting + # shellcheck disable=SC2046 + javac -proc:only -nowarn $(find . -name "*.java") \ + || echo "${testcase}/expected issues one or more errors, which are printed above." ; returnval=2 + cd ../.. || exit 1 +done + +if [ "${returnval}" == 0 ]; then + echo "All expected test outputs compiled successfully." +elif [ "${returnval}" == 2 ]; then + echo "Some expected test outputs do not compile successfully. See the above error output for details." +fi + +exit ${returnval} \ No newline at end of file From d547f2e0238e8e29a6c5e29787cbb0b1b97f4370 Mon Sep 17 00:00:00 2001 From: Martin Kellogg Date: Wed, 22 Nov 2023 10:16:51 -0500 Subject: [PATCH 02/14] wrong task type --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5d8d5badd..8366829db 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,7 @@ task requireJavadoc(type: JavaExec) { args "src/main/java" } -task expectedTestOutputsMustCompile(type: Verification) { +task expectedTestOutputsMustCompile(type: Exec) { // TODO: should this task run in CI, or as part of the regular tests? commandLine "sh", "typecheck_test_outputs.sh" } From 71d6ca5e24c3982393a03b69a447e80d99612a08 Mon Sep 17 00:00:00 2001 From: Martin Kellogg Date: Wed, 22 Nov 2023 10:17:54 -0500 Subject: [PATCH 03/14] clean up + correct comments --- typecheck_test_outputs.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/typecheck_test_outputs.sh b/typecheck_test_outputs.sh index 783f42468..3232d09ca 100755 --- a/typecheck_test_outputs.sh +++ b/typecheck_test_outputs.sh @@ -1,7 +1,9 @@ #!/bin/sh # This script runs javac on all of the expected test outputs under src/test/resources. -# It returns 1 if any of them fail to compile, and zero if all of them do compile. +# It returns 2 if any of them fail to compile, 1 if there are any malformed test directories, +# and 0 if all of them do compile. +# # It is desirable that all of the expected test outputs compile, because Specimin # should produce independently-compilable programs. From 12e817f734db7180906ccf10b54e3773e5d65c5b Mon Sep 17 00:00:00 2001 From: Martin Kellogg Date: Wed, 22 Nov 2023 10:18:40 -0500 Subject: [PATCH 04/14] remove unused variable --- typecheck_test_outputs.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/typecheck_test_outputs.sh b/typecheck_test_outputs.sh index 3232d09ca..f13db92d3 100755 --- a/typecheck_test_outputs.sh +++ b/typecheck_test_outputs.sh @@ -12,7 +12,6 @@ returnval=0 cd src/test/resources || exit 1 for testcase in * ; do cd "${testcase}/expected/" || exit 1 - javaccmd="javac -proc:only -nowarn $(find . -name "*.java")" # javac relies on word splitting # shellcheck disable=SC2046 javac -proc:only -nowarn $(find . -name "*.java") \ From 49c041789e68192f2e662831299ed4c187ec753e Mon Sep 17 00:00:00 2001 From: Martin Kellogg Date: Wed, 22 Nov 2023 10:21:54 -0500 Subject: [PATCH 05/14] clarify diagnostic --- typecheck_test_outputs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typecheck_test_outputs.sh b/typecheck_test_outputs.sh index f13db92d3..e56fce980 100755 --- a/typecheck_test_outputs.sh +++ b/typecheck_test_outputs.sh @@ -15,7 +15,7 @@ for testcase in * ; do # javac relies on word splitting # shellcheck disable=SC2046 javac -proc:only -nowarn $(find . -name "*.java") \ - || echo "${testcase}/expected issues one or more errors, which are printed above." ; returnval=2 + || echo "Running javac on ${testcase}/expected issues one or more errors, which are printed above." ; returnval=2 cd ../.. || exit 1 done From 5334f0c4e65297bd9a6a4c1bbd1ad1fe52a9d7ad Mon Sep 17 00:00:00 2001 From: Martin Kellogg Date: Wed, 22 Nov 2023 10:22:52 -0500 Subject: [PATCH 06/14] fix shellcheck warning --- typecheck_test_outputs.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typecheck_test_outputs.sh b/typecheck_test_outputs.sh index e56fce980..436d9c3bd 100755 --- a/typecheck_test_outputs.sh +++ b/typecheck_test_outputs.sh @@ -19,9 +19,9 @@ for testcase in * ; do cd ../.. || exit 1 done -if [ "${returnval}" == 0 ]; then +if [ "${returnval}" = 0 ]; then echo "All expected test outputs compiled successfully." -elif [ "${returnval}" == 2 ]; then +elif [ "${returnval}" = 2 ]; then echo "Some expected test outputs do not compile successfully. See the above error output for details." fi From 536cf51ea75fc3a68df6a4dd618bada0b7408a6a Mon Sep 17 00:00:00 2001 From: Martin Kellogg Date: Wed, 22 Nov 2023 14:55:26 -0500 Subject: [PATCH 07/14] make synthetic class outputs deterministic --- .../specimin/UnsolvedClass.java | 19 +++++++++++++------ .../expected/org/wild/Mammal.java | 4 +++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedClass.java b/src/main/java/org/checkerframework/specimin/UnsolvedClass.java index 087927140..46068d550 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedClass.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedClass.java @@ -3,6 +3,7 @@ import com.google.common.base.Splitter; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.checkerframework.checker.signature.qual.ClassGetSimpleName; @@ -12,14 +13,20 @@ * The reason is that the class file is not in the root directory. */ public class UnsolvedClass { - /** Set of methods belongs to the class */ - private final Set methods; + /** + * Set of methods belongs to the class. Must be a linked set to ensure deterministic iteration + * order when writing files synthetic classes. + */ + private final LinkedHashSet methods; /** The name of the class */ private final @ClassGetSimpleName String className; - /** The fields of this class */ - private final Set classFields; + /** + * The fields of this class. Must be a linked set to ensure deterministic iteration order when + * writing files for synthetic classes. + */ + private final LinkedHashSet classFields; /** * The name of the package of the class. We rely on the import statements from the source codes to @@ -35,9 +42,9 @@ public class UnsolvedClass { */ public UnsolvedClass(@ClassGetSimpleName String className, String packageName) { this.className = className; - this.methods = new HashSet<>(); + this.methods = new LinkedHashSet<>(); this.packageName = packageName; - this.classFields = new HashSet<>(); + this.classFields = new LinkedHashSet<>(); } /** diff --git a/src/test/resources/syntheticsupervariables/expected/org/wild/Mammal.java b/src/test/resources/syntheticsupervariables/expected/org/wild/Mammal.java index fb724e63f..9661e3de6 100644 --- a/src/test/resources/syntheticsupervariables/expected/org/wild/Mammal.java +++ b/src/test/resources/syntheticsupervariables/expected/org/wild/Mammal.java @@ -1,5 +1,7 @@ package org.wild; public class Mammal { - public boolean canBreathUnderWater = false; + public String habitat = null; + + public boolean canBreathUnderWater = false; } From b73df0da556d71ea7b265549b1a6a435e4bd0569 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Nov 2023 15:15:19 -0500 Subject: [PATCH 08/14] remove unused import statements --- .../specimin/MethodPrunerVisitor.java | 25 ++++++++++++++++++- .../specimin/SpeciminRunner.java | 3 ++- .../expected/com/example/Simple.java | 2 -- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java b/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java index 1345c0208..05eafda9d 100644 --- a/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java +++ b/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java @@ -1,6 +1,8 @@ package org.checkerframework.specimin; import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.ImportDeclaration; +import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.visitor.ModifierVisitor; @@ -28,6 +30,13 @@ public class MethodPrunerVisitor extends ModifierVisitor { */ private Set methodsToEmpty; + /** + * This is the set of classes used by the target methods. We use this set to determine if we + * should keep or delete an import statement. The string representing the class are in + * the @FullyQualifiedName form. + */ + private Set classesUsedByTargetMethods; + /** * This boolean tracks whether the element currently being visited is inside a target method. It * is set by {@link #visit(MethodDeclaration, Void)}. @@ -43,9 +52,23 @@ public class MethodPrunerVisitor extends ModifierVisitor { * methods for specimin) * @param methodsToEmpty the set of methods whose bodies should be removed */ - public MethodPrunerVisitor(Set methodsToKeep, Set methodsToEmpty) { + public MethodPrunerVisitor( + Set methodsToKeep, + Set methodsToEmpty, + Set classesUsedByTargetMethods) { this.methodsToLeaveUnchanged = methodsToKeep; this.methodsToEmpty = methodsToEmpty; + this.classesUsedByTargetMethods = classesUsedByTargetMethods; + } + + @Override + public Node visit(ImportDeclaration decl, Void p) { + String classFullName = decl.getNameAsString(); + if (classesUsedByTargetMethods.contains(classFullName)) { + return super.visit(decl, p); + } + decl.remove(); + return decl; } @Override diff --git a/src/main/java/org/checkerframework/specimin/SpeciminRunner.java b/src/main/java/org/checkerframework/specimin/SpeciminRunner.java index a489ef42d..6adfb8fb2 100644 --- a/src/main/java/org/checkerframework/specimin/SpeciminRunner.java +++ b/src/main/java/org/checkerframework/specimin/SpeciminRunner.java @@ -176,7 +176,8 @@ public static void performMinimization( } MethodPrunerVisitor methodPruner = - new MethodPrunerVisitor(finder.getTargetMethods(), finder.getUsedMembers()); + new MethodPrunerVisitor( + finder.getTargetMethods(), finder.getUsedMembers(), finder.getUsedClass()); for (CompilationUnit cu : parsedTargetFiles.values()) { cu.accept(methodPruner, null); diff --git a/src/test/resources/sameclassname/expected/com/example/Simple.java b/src/test/resources/sameclassname/expected/com/example/Simple.java index b3f45034a..0cfb43b65 100644 --- a/src/test/resources/sameclassname/expected/com/example/Simple.java +++ b/src/test/resources/sameclassname/expected/com/example/Simple.java @@ -1,7 +1,5 @@ package com.example; -import org.first.Calculator; - class Simple { public void secondCalculator () { org.second.Calculator thisCal = new org.second.Calculator(); From c8dca2e87401402cba217df7b0fecba774d9e624 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Nov 2023 15:18:46 -0500 Subject: [PATCH 09/14] fix English grammar --- .../java/org/checkerframework/specimin/MethodPrunerVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java b/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java index 05eafda9d..156a8fff2 100644 --- a/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java +++ b/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java @@ -32,7 +32,7 @@ public class MethodPrunerVisitor extends ModifierVisitor { /** * This is the set of classes used by the target methods. We use this set to determine if we - * should keep or delete an import statement. The string representing the class are in + * should keep or delete an import statement. The strings representing the classes are in * the @FullyQualifiedName form. */ private Set classesUsedByTargetMethods; From b080243855a27fb3e4b03a423253b5301d0f0c31 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 24 Nov 2023 17:34:29 -0500 Subject: [PATCH 10/14] use qualified names when correct types --- .../specimin/GetTypesFullNameVisitor.java | 63 +++++++++++++++++ .../specimin/JavaTypeCorrect.java | 70 +++++++++---------- .../specimin/SpeciminRunner.java | 9 ++- .../specimin/UnsolvedClass.java | 3 +- .../specimin/UnsolvedMethod.java | 10 ++- .../specimin/UnsolvedSymbolVisitor.java | 3 +- .../expected/com/example/Parent.java | 2 +- .../expected/com/example/Maternal.java | 2 +- .../expected/com/example/Paternal.java | 2 +- .../expected/com/example/Maternal.java | 2 +- .../expected/com/example/Paternal.java | 2 +- 11 files changed, 117 insertions(+), 51 deletions(-) create mode 100644 src/main/java/org/checkerframework/specimin/GetTypesFullNameVisitor.java diff --git a/src/main/java/org/checkerframework/specimin/GetTypesFullNameVisitor.java b/src/main/java/org/checkerframework/specimin/GetTypesFullNameVisitor.java new file mode 100644 index 000000000..20c97bb55 --- /dev/null +++ b/src/main/java/org/checkerframework/specimin/GetTypesFullNameVisitor.java @@ -0,0 +1,63 @@ +package org.checkerframework.specimin; + +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.github.javaparser.ast.visitor.ModifierVisitor; +import com.github.javaparser.ast.visitor.Visitable; +import com.github.javaparser.resolution.UnsolvedSymbolException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A visitor that traverses a Java file's AST and creates a map, associating the file name with the + * set of types used inside it. + */ +public class GetTypesFullNameVisitor extends ModifierVisitor { + + /** The directory path of the Java file. */ + private String fileDirectory = ""; + + /** + * A map that associates the file directory with the set of fully qualified names of types used + * within that file. + */ + private Map> fileAndAssociatedTypes = new HashMap<>(); + + /** + * Get the map of files' directories and types used within those files. + * + * @return the value of fileAndAssociatedTypes + */ + public Map> getFileAndAssociatedTypes() { + return fileAndAssociatedTypes; + } + + @Override + public Visitable visit(ClassOrInterfaceDeclaration decl, Void p) { + // Nested type classes don't have a separate class file. + if (!decl.isNestedType()) { + fileDirectory = decl.getFullyQualifiedName().get().replace(".", "/") + ".java"; + fileAndAssociatedTypes.put(fileDirectory, new HashSet<>()); + } + return super.visit(decl, p); + } + + @Override + public Visitable visit(ClassOrInterfaceType type, Void p) { + String typeFullName; + try { + typeFullName = type.resolve().getQualifiedName(); + } catch (UnsolvedSymbolException e) { + return super.visit(type, p); + } + if (fileAndAssociatedTypes.containsKey(fileDirectory)) { + fileAndAssociatedTypes.get(fileDirectory).add(typeFullName); + return super.visit(type, p); + } else { + throw new RuntimeException( + "Unexpected files and types: " + fileDirectory + ", " + typeFullName); + } + } +} diff --git a/src/main/java/org/checkerframework/specimin/JavaTypeCorrect.java b/src/main/java/org/checkerframework/specimin/JavaTypeCorrect.java index 5d7f84f03..3104b9b11 100644 --- a/src/main/java/org/checkerframework/specimin/JavaTypeCorrect.java +++ b/src/main/java/org/checkerframework/specimin/JavaTypeCorrect.java @@ -9,8 +9,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.checkerframework.checker.signature.qual.ClassGetSimpleName; -import org.checkerframework.checker.signature.qual.DotSeparatedIdentifiers; /** * This class uses javac to analyze files. If there are any incompatible type errors in those files, @@ -34,7 +32,13 @@ class JavaTypeCorrect { * This map is for type correcting. The key is the name of the current incorrect type, and the * value is the name of the desired correct type. */ - private Map<@ClassGetSimpleName String, @ClassGetSimpleName String> typeToChange; + private Map typeToChange; + + /** + * A map that associates the file directory with the set of fully qualified names of types used + * within that file. + */ + private Map> fileAndAssociatedTypes = new HashMap<>(); /** * Create a new JavaTypeCorrect instance. The directories of files in fileNameList are relative to @@ -43,10 +47,14 @@ class JavaTypeCorrect { * @param rootDirectory the root directory of the files to correct types * @param fileNameList the list of the relative directory of the files to correct types */ - public JavaTypeCorrect(String rootDirectory, Set fileNameList) { + public JavaTypeCorrect( + String rootDirectory, + Set fileNameList, + Map> fileAndAssociatedTypes) { this.fileNameList = fileNameList; this.sourcePath = new File(rootDirectory).getAbsolutePath(); this.typeToChange = new HashMap<>(); + this.fileAndAssociatedTypes = fileAndAssociatedTypes; } /** @@ -54,7 +62,7 @@ public JavaTypeCorrect(String rootDirectory, Set fileNameList) { * * @return the value of typeToChange */ - public Map<@ClassGetSimpleName String, @ClassGetSimpleName String> getTypeToChange() { + public Map getTypeToChange() { return typeToChange; } @@ -87,7 +95,7 @@ public void runJavacAndUpdateTypes(String filePath) { String line; while ((line = reader.readLine()) != null) { if (line.contains("error: incompatible types")) { - updateTypeToChange(line); + updateTypeToChange(line, filePath); } } } catch (Exception e) { @@ -99,8 +107,9 @@ public void runJavacAndUpdateTypes(String filePath) { * This method updates typeToChange by relying on the error messages from javac * * @param errorMessage the error message to be analyzed + * @param filePath the path of the file where this error happens */ - private void updateTypeToChange(String errorMessage) { + private void updateTypeToChange(String errorMessage, String filePath) { List splitErrorMessage = Splitter.onPattern("\\s+").splitToList(errorMessage); if (splitErrorMessage.size() < 7) { throw new RuntimeException("Unexpected type error messages: " + errorMessage); @@ -110,38 +119,29 @@ private void updateTypeToChange(String errorMessage) { * 2. error: incompatible types: found required */ if (errorMessage.contains("cannot be converted to")) { - // since this is from javac, we know that these will be dot-separated identifiers. - @SuppressWarnings("signature") - @DotSeparatedIdentifiers String incorrectType = splitErrorMessage.get(4); - @SuppressWarnings("signature") - @DotSeparatedIdentifiers String correctType = splitErrorMessage.get(splitErrorMessage.size() - 1); - typeToChange.put(toSimpleName(incorrectType), toSimpleName(correctType)); + String incorrectType = splitErrorMessage.get(4); + String correctType = splitErrorMessage.get(splitErrorMessage.size() - 1); + typeToChange.put(incorrectType, tryResolveFullyQualifiedType(correctType, filePath)); } else { - @SuppressWarnings("signature") - @DotSeparatedIdentifiers String incorrectType = splitErrorMessage.get(5); - @SuppressWarnings("signature") - @DotSeparatedIdentifiers String correctType = splitErrorMessage.get(splitErrorMessage.size() - 1); - typeToChange.put(toSimpleName(incorrectType), toSimpleName(correctType)); + String incorrectType = splitErrorMessage.get(5); + String correctType = splitErrorMessage.get(splitErrorMessage.size() - 1); + typeToChange.put(incorrectType, tryResolveFullyQualifiedType(correctType, filePath)); } } - /** - * This method takes the name of a class and converts it to the @ClassGetSimpleName type according - * to Checker Framework. If the name is already in the @ClassGetSimpleName form, this method will - * not make any changes - * - * @param className the name of the class to be converted - * @return the simple name of the class - */ - // the code is self-explanatory, essentially the last element of a class name is the simple name - // of that class. This method takes the input from the error message of javac, so we know that - // className will be a dot-separated identifier. - @SuppressWarnings("signature") - public static @ClassGetSimpleName String toSimpleName(@DotSeparatedIdentifiers String className) { - List classNameParts = Splitter.onPattern("[.]").splitToList(className); - if (classNameParts.size() < 2) { - return className; + public String tryResolveFullyQualifiedType(String type, String filePath) { + // type is already in the fully qualifed format + if (Splitter.onPattern("\\.").splitToList(type).size() > 1) { + return type; + } + if (fileAndAssociatedTypes.containsKey(filePath)) { + Set fullyQualifiedType = fileAndAssociatedTypes.get(filePath); + for (String typeFullName : fullyQualifiedType) { + if (typeFullName.substring(typeFullName.lastIndexOf(".") + 1).equals(type)) { + return typeFullName; + } + } } - return classNameParts.get(classNameParts.size() - 1); + return type; } } diff --git a/src/main/java/org/checkerframework/specimin/SpeciminRunner.java b/src/main/java/org/checkerframework/specimin/SpeciminRunner.java index 6adfb8fb2..501317adb 100644 --- a/src/main/java/org/checkerframework/specimin/SpeciminRunner.java +++ b/src/main/java/org/checkerframework/specimin/SpeciminRunner.java @@ -162,8 +162,15 @@ public static void performMinimization( relatedClass.add(directoryOfFile); } } + GetTypesFullNameVisitor getTypesFullNameVisitor = new GetTypesFullNameVisitor(); + for (CompilationUnit cu : parsedTargetFiles.values()) { + cu.accept(getTypesFullNameVisitor, null); + } + Map> filesAndAssociatedTypes = + getTypesFullNameVisitor.getFileAndAssociatedTypes(); // correct the types of all related files before adding them to parsedTargetFiles - JavaTypeCorrect typeCorrecter = new JavaTypeCorrect(root, relatedClass); + JavaTypeCorrect typeCorrecter = + new JavaTypeCorrect(root, relatedClass, filesAndAssociatedTypes); typeCorrecter.correctTypesForAllFiles(); addMissingClass.updateTypes(typeCorrecter.getTypeToChange()); diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedClass.java b/src/main/java/org/checkerframework/specimin/UnsolvedClass.java index 087927140..1c259f0a1 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedClass.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedClass.java @@ -102,8 +102,7 @@ public void addFields(String variableExpression) { * @param currentReturnType the current return type of this method * @param desiredReturnType the new return type */ - public void updateMethodByReturnType( - @ClassGetSimpleName String currentReturnType, @ClassGetSimpleName String desiredReturnType) { + public void updateMethodByReturnType(String currentReturnType, String desiredReturnType) { for (UnsolvedMethod method : methods) { if (method.getReturnType().equals(currentReturnType)) { method.setReturnType(desiredReturnType); diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedMethod.java b/src/main/java/org/checkerframework/specimin/UnsolvedMethod.java index fa2a4588b..96192682b 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedMethod.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedMethod.java @@ -1,7 +1,6 @@ package org.checkerframework.specimin; import java.util.List; -import org.checkerframework.checker.signature.qual.ClassGetSimpleName; /** * An UnsolvedMethod instance is a representation of a method that can not be solved by @@ -15,7 +14,7 @@ public class UnsolvedMethod { * The return type of the method. At the moment, we set the return type the same as the class * where the method belongs to. */ - private @ClassGetSimpleName String returnType; + private String returnType; /** * The list of parameters of the method. (Right now we won't touch it until the new variant of @@ -30,8 +29,7 @@ public class UnsolvedMethod { * @param returnType the return type of the method * @param parameterList the list of parameters for this method */ - public UnsolvedMethod( - String name, @ClassGetSimpleName String returnType, List parameterList) { + public UnsolvedMethod(String name, String returnType, List parameterList) { this.name = name; this.returnType = returnType; this.parameterList = parameterList; @@ -43,7 +41,7 @@ public UnsolvedMethod( * * @param returnType the return type to bet set for this method */ - public void setReturnType(@ClassGetSimpleName String returnType) { + public void setReturnType(String returnType) { this.returnType = returnType; } @@ -52,7 +50,7 @@ public void setReturnType(@ClassGetSimpleName String returnType) { * * @return the value of returnType */ - public @ClassGetSimpleName String getReturnType() { + public String getReturnType() { return returnType; } diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java b/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java index 2146db792..0e9cc5c70 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java @@ -1299,8 +1299,7 @@ public void updateClassSetWithNotSimpleMethodCall(MethodCallExpr method) { * * @param typeToCorrect the Map to be analyzed */ - public void updateTypes( - Map<@ClassGetSimpleName String, @ClassGetSimpleName String> typeToCorrect) { + public void updateTypes(Map typeToCorrect) { for (String incorrectType : typeToCorrect.keySet()) { // convert MethodNameReturnType to methodName String involvedMethod = diff --git a/src/test/resources/HiddenSuperFieldAndUnsolvedLocal/expected/com/example/Parent.java b/src/test/resources/HiddenSuperFieldAndUnsolvedLocal/expected/com/example/Parent.java index 1a3880198..7b2b95a38 100644 --- a/src/test/resources/HiddenSuperFieldAndUnsolvedLocal/expected/com/example/Parent.java +++ b/src/test/resources/HiddenSuperFieldAndUnsolvedLocal/expected/com/example/Parent.java @@ -2,5 +2,5 @@ public class Parent { - public MyType theName = null; + public sample.pack.MyType theName = null; } diff --git a/src/test/resources/innerclass/expected/com/example/Maternal.java b/src/test/resources/innerclass/expected/com/example/Maternal.java index 62d581f45..e4c90760e 100644 --- a/src/test/resources/innerclass/expected/com/example/Maternal.java +++ b/src/test/resources/innerclass/expected/com/example/Maternal.java @@ -2,5 +2,5 @@ public class Maternal { - public String maternalLastName = null; + public java.lang.String maternalLastName = null; } diff --git a/src/test/resources/innerclass/expected/com/example/Paternal.java b/src/test/resources/innerclass/expected/com/example/Paternal.java index b1ac01537..068888a8d 100644 --- a/src/test/resources/innerclass/expected/com/example/Paternal.java +++ b/src/test/resources/innerclass/expected/com/example/Paternal.java @@ -2,5 +2,5 @@ public class Paternal { - public String paternalLastName = null; + public java.lang.String paternalLastName = null; } diff --git a/src/test/resources/staticnestedclass/expected/com/example/Maternal.java b/src/test/resources/staticnestedclass/expected/com/example/Maternal.java index 62d581f45..e4c90760e 100644 --- a/src/test/resources/staticnestedclass/expected/com/example/Maternal.java +++ b/src/test/resources/staticnestedclass/expected/com/example/Maternal.java @@ -2,5 +2,5 @@ public class Maternal { - public String maternalLastName = null; + public java.lang.String maternalLastName = null; } diff --git a/src/test/resources/staticnestedclass/expected/com/example/Paternal.java b/src/test/resources/staticnestedclass/expected/com/example/Paternal.java index b1ac01537..068888a8d 100644 --- a/src/test/resources/staticnestedclass/expected/com/example/Paternal.java +++ b/src/test/resources/staticnestedclass/expected/com/example/Paternal.java @@ -2,5 +2,5 @@ public class Paternal { - public String paternalLastName = null; + public java.lang.String paternalLastName = null; } From 184c2b767a0f073be12c54c22d598d76f514c145 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 24 Nov 2023 17:39:19 -0500 Subject: [PATCH 11/14] Add javadoc --- .../org/checkerframework/specimin/JavaTypeCorrect.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/checkerframework/specimin/JavaTypeCorrect.java b/src/main/java/org/checkerframework/specimin/JavaTypeCorrect.java index 3104b9b11..86845b213 100644 --- a/src/main/java/org/checkerframework/specimin/JavaTypeCorrect.java +++ b/src/main/java/org/checkerframework/specimin/JavaTypeCorrect.java @@ -129,6 +129,15 @@ private void updateTypeToChange(String errorMessage, String filePath) { } } + /** + * This method tries to get the fully-qualified name of a type based on the simple name of that + * type and the class file where that type is used. + * + * @param type the type to be taken as input + * @param filePath the path of the file where type is used + * @return the fully-qualified name of that type if any. Otherwise, return the original expression + * of type. + */ public String tryResolveFullyQualifiedType(String type, String filePath) { // type is already in the fully qualifed format if (Splitter.onPattern("\\.").splitToList(type).size() > 1) { From ce9bb8fa07549cff5b6ba233be8addba3db64108 Mon Sep 17 00:00:00 2001 From: Loi Nguyen <113363230+LoiNguyenCS@users.noreply.github.com> Date: Fri, 24 Nov 2023 17:45:46 -0500 Subject: [PATCH 12/14] add misssing param --- .../java/org/checkerframework/specimin/MethodPrunerVisitor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java b/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java index 156a8fff2..ba847660f 100644 --- a/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java +++ b/src/main/java/org/checkerframework/specimin/MethodPrunerVisitor.java @@ -51,6 +51,7 @@ public class MethodPrunerVisitor extends ModifierVisitor { * @param methodsToKeep the set of methods whose bodies should be kept intact (usually the target * methods for specimin) * @param methodsToEmpty the set of methods whose bodies should be removed + * @param classesUsedByTargetMethods the classes used by target methods */ public MethodPrunerVisitor( Set methodsToKeep, From 721daa05f9884eabb848f0ca3ae442a49ee7246d Mon Sep 17 00:00:00 2001 From: Loi Nguyen <113363230+LoiNguyenCS@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:24:35 -0500 Subject: [PATCH 13/14] add {} to typecheck_test_outputs.sh --- typecheck_test_outputs.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typecheck_test_outputs.sh b/typecheck_test_outputs.sh index 436d9c3bd..ebb0ee8ac 100755 --- a/typecheck_test_outputs.sh +++ b/typecheck_test_outputs.sh @@ -15,7 +15,7 @@ for testcase in * ; do # javac relies on word splitting # shellcheck disable=SC2046 javac -proc:only -nowarn $(find . -name "*.java") \ - || echo "Running javac on ${testcase}/expected issues one or more errors, which are printed above." ; returnval=2 + || { echo "Running javac on ${testcase}/expected issues one or more errors, which are printed above."; returnval=2; } cd ../.. || exit 1 done @@ -25,4 +25,4 @@ elif [ "${returnval}" = 2 ]; then echo "Some expected test outputs do not compile successfully. See the above error output for details." fi -exit ${returnval} \ No newline at end of file +exit ${returnval} From a932b0980f959274c941ee597df56e80ffc85899 Mon Sep 17 00:00:00 2001 From: Loi Nguyen <113363230+LoiNguyenCS@users.noreply.github.com> Date: Mon, 27 Nov 2023 11:25:51 -0500 Subject: [PATCH 14/14] use unmodifiableMap --- .../org/checkerframework/specimin/GetTypesFullNameVisitor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/checkerframework/specimin/GetTypesFullNameVisitor.java b/src/main/java/org/checkerframework/specimin/GetTypesFullNameVisitor.java index 20c97bb55..19e083e44 100644 --- a/src/main/java/org/checkerframework/specimin/GetTypesFullNameVisitor.java +++ b/src/main/java/org/checkerframework/specimin/GetTypesFullNameVisitor.java @@ -5,6 +5,7 @@ import com.github.javaparser.ast.visitor.ModifierVisitor; import com.github.javaparser.ast.visitor.Visitable; import com.github.javaparser.resolution.UnsolvedSymbolException; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -31,7 +32,7 @@ public class GetTypesFullNameVisitor extends ModifierVisitor { * @return the value of fileAndAssociatedTypes */ public Map> getFileAndAssociatedTypes() { - return fileAndAssociatedTypes; + return Collections.unmodifiableMap(fileAndAssociatedTypes); } @Override