From 90a4b521f0c1147dc988372c3d858e108cb24b18 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 1 Dec 2023 17:03:02 -0500 Subject: [PATCH 1/5] add codes and tests for unsolved field access --- .../specimin/TargetMethodFinderVisitor.java | 1 + .../specimin/UnsolvedClass.java | 20 ++- .../specimin/UnsolvedSymbolVisitor.java | 145 +++++++++++++++++- .../UnsolvedStaticQualifiedField.java | 18 +++ .../specimin/UnsolvedStaticSimpleField.java | 18 +++ .../UnsolvedStaticSimplePrimitiveField.java | 18 +++ .../expected/com/example/Foo.java | 11 ++ .../expected/org/sampling/Baz.java | 10 ++ .../org/sampling/DoAdditionReturnType.java | 4 + .../org/sampling/OrgSamplingBazCalType.java | 8 + .../input/com/example/Foo.java | 8 + .../expected/com/example/Foo.java | 8 + .../expected/org/sampling/Baz.java | 6 + .../org/sampling/DoAdditionReturnType.java | 4 + .../sampling/OrgSamplingBazMyFieldType.java | 8 + .../input/com/example/Foo.java | 8 + .../expected/com/example/Foo.java | 10 ++ .../expected/org/sampling/Baz.java | 6 + .../org/sampling/DoAdditionReturnType.java | 4 + .../sampling/OrgSamplingBazMyFieldType.java | 8 + .../input/com/example/Foo.java | 8 + .../expected/com/example/Foo.java | 10 ++ .../expected/org/sampling/Baz.java | 6 + .../input/com/example/Foo.java | 8 + 24 files changed, 348 insertions(+), 7 deletions(-) create mode 100644 src/test/java/org/checkerframework/specimin/UnsolvedStaticQualifiedField.java create mode 100644 src/test/java/org/checkerframework/specimin/UnsolvedStaticSimpleField.java create mode 100644 src/test/java/org/checkerframework/specimin/UnsolvedStaticSimplePrimitiveField.java create mode 100644 src/test/resources/unsolvednonstaticfield/expected/com/example/Foo.java create mode 100644 src/test/resources/unsolvednonstaticfield/expected/org/sampling/Baz.java create mode 100644 src/test/resources/unsolvednonstaticfield/expected/org/sampling/DoAdditionReturnType.java create mode 100644 src/test/resources/unsolvednonstaticfield/expected/org/sampling/OrgSamplingBazCalType.java create mode 100644 src/test/resources/unsolvednonstaticfield/input/com/example/Foo.java create mode 100644 src/test/resources/unsolvedstaticqualifiedfield/expected/com/example/Foo.java create mode 100644 src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/Baz.java create mode 100644 src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/DoAdditionReturnType.java create mode 100644 src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/OrgSamplingBazMyFieldType.java create mode 100644 src/test/resources/unsolvedstaticqualifiedfield/input/com/example/Foo.java create mode 100644 src/test/resources/unsolvedstaticsimplefield/expected/com/example/Foo.java create mode 100644 src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/Baz.java create mode 100644 src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/DoAdditionReturnType.java create mode 100644 src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/OrgSamplingBazMyFieldType.java create mode 100644 src/test/resources/unsolvedstaticsimplefield/input/com/example/Foo.java create mode 100644 src/test/resources/unsolvedstaticsimpleprimitivefield/expected/com/example/Foo.java create mode 100644 src/test/resources/unsolvedstaticsimpleprimitivefield/expected/org/sampling/Baz.java create mode 100644 src/test/resources/unsolvedstaticsimpleprimitivefield/input/com/example/Foo.java diff --git a/src/main/java/org/checkerframework/specimin/TargetMethodFinderVisitor.java b/src/main/java/org/checkerframework/specimin/TargetMethodFinderVisitor.java index 81546f51b..a86963085 100644 --- a/src/main/java/org/checkerframework/specimin/TargetMethodFinderVisitor.java +++ b/src/main/java/org/checkerframework/specimin/TargetMethodFinderVisitor.java @@ -306,6 +306,7 @@ public Visitable visit(FieldAccessExpr expr, Void p) { fullNameOfClass = expr.resolve().asField().declaringType().getQualifiedName(); usedMembers.add(fullNameOfClass + "#" + expr.getName().asString()); usedClass.add(fullNameOfClass); + usedClass.add(expr.resolve().getType().describe()); } catch (UnsolvedSymbolException e) { // if the a field is accessed in the form of a fully-qualified path, such as // org.example.A.b, then other components in the path apart from the class name and field diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedClass.java b/src/main/java/org/checkerframework/specimin/UnsolvedClass.java index e9df67ea2..f83342649 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedClass.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedClass.java @@ -164,7 +164,13 @@ public void updateFieldByType(String currentType, String correctType) { Iterator iterator = classFields.iterator(); Set newFields = new HashSet<>(); while (iterator.hasNext()) { - List elements = Splitter.on(' ').splitToList(iterator.next()); + String fieldDeclared = iterator.next(); + boolean isStatic = false; + if (fieldDeclared.startsWith("static")) { + fieldDeclared = fieldDeclared.replace("static ", ""); + isStatic = true; + } + List elements = Splitter.on(' ').splitToList(fieldDeclared); // fieldExpression is guaranteed to have the form "TYPE FIELD_NAME". Since this field // expression is from a synthetic class, there is no annotation involved, so TYPE has no // space. @@ -172,9 +178,15 @@ public void updateFieldByType(String currentType, String correctType) { String fieldName = elements.get(1); if (fieldType.equals(currentType)) { iterator.remove(); - newFields.add( - UnsolvedSymbolVisitor.setInitialValueForVariableDeclaration( - correctType, correctType + " " + fieldName)); + if (!isStatic) { + newFields.add( + UnsolvedSymbolVisitor.setInitialValueForVariableDeclaration( + correctType, correctType + " " + fieldName)); + } else { + newFields.add( + UnsolvedSymbolVisitor.setInitialValueForVariableDeclaration( + correctType, "static " + correctType + " " + fieldName)); + } } } diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java b/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java index ada34d229..b6ec341ef 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java @@ -120,6 +120,12 @@ public class UnsolvedSymbolVisitor extends ModifierVisitor { /** This instance maps the name of a synthetic method with its synthetic class */ private final Map syntheticMethodAndClass = new HashMap<>(); + /** + * This instance maps the name of a synthetic type with the class where there is a field declared + * with that type + */ + private final Map syntheticTypeAndClass = new HashMap<>(); + /** * This is to check if the current synthetic files are enough to prevent UnsolvedSymbolException * or we still need more. @@ -561,6 +567,30 @@ public Visitable visit(MethodDeclaration node, Void arg) { public Visitable visit(FieldAccessExpr node, Void p) { if (isASuperCall(node) && !canBeSolved(node)) { updateSyntheticClassForSuperCall(node); + } else if (canBeSolved(node)) { + return super.visit(node, p); + } else if (isAQualifiedFieldSignature(node.toString())) { + updateClassSetWithQualifiedFieldSignature(node.toString(), true); + } else if (unsolvedFieldCalledByASimpleClassName(node)) { + String simpleClassName = node.getScope().toString(); + String fullyQualifiedCall = + classAndPackageMap.getOrDefault(simpleClassName, currentPackage) + "." + node; + updateClassSetWithQualifiedFieldSignature(fullyQualifiedCall, true); + } + // if the symbol that called this field is solvable yet this field is unsolved, then the type of + // the calling symbol is a synthetic class that needs to have a synthetic field updated + else if (canBeSolved(node.getScope())) { + updateSyntheticClassWithNonStaticFields(node); + } + + try { + node.resolve(); + } catch (UnsolvedSymbolException 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())) { + this.gotException = true; + } } return super.visit(node, p); } @@ -1001,6 +1031,19 @@ public HashSet getParameterFromAMethodDeclaration(MethodDeclaration decl return setOfParameters; } + /** + * Given a non-static and unsolved field access expression, this method will update the + * corresponding synthetic class. + * + * @param field a non-static field access expression + */ + public void updateSyntheticClassWithNonStaticFields(FieldAccessExpr field) { + Expression caller = field.getScope(); + String fullyQualifiedClassName = caller.calculateResolvedType().describe(); + String fieldQualifedSignature = fullyQualifiedClassName + "." + field.getNameAsString(); + updateClassSetWithQualifiedFieldSignature(fieldQualifedSignature, false); + } + /** * For a super call, this method will update the corresponding synthetic class * @@ -1018,13 +1061,13 @@ public void updateSyntheticClassForSuperCall(Expression expr) { methodAndReturnType.getOrDefault(expr.asMethodCallExpr().getNameAsString(), "")); } else if (expr instanceof FieldAccessExpr) { String nameAsString = expr.asFieldAccessExpr().getNameAsString(); - updateUnsolvedClassWithFields( + updateUnsolvedSuperClassWithFields( nameAsString, getParentClass(className), classAndPackageMap.getOrDefault(getParentClass(className), this.currentPackage)); } else if (expr instanceof NameExpr) { String nameAsString = expr.asNameExpr().getNameAsString(); - updateUnsolvedClassWithFields( + updateUnsolvedSuperClassWithFields( nameAsString, getParentClass(className), classAndPackageMap.getOrDefault(getParentClass(className), this.currentPackage)); @@ -1044,7 +1087,7 @@ public void updateSyntheticClassForSuperCall(Expression expr) { * @param className the name of the synthetic class * @param packageName the package of the synthetic class */ - public void updateUnsolvedClassWithFields( + public void updateUnsolvedSuperClassWithFields( String var, @ClassGetSimpleName String className, String packageName) { UnsolvedClass relatedClass = new UnsolvedClass(className, packageName); if (variablesAndDeclaration.containsKey(var)) { @@ -1465,6 +1508,24 @@ public boolean unsolvedAndCalledByASimpleClassName(MethodCallExpr method) { } } + /** + * Check whether a field, invoked by a simple class name, is unsolved + * + * @param field the field to be checked + * @return true if the field is unsolved and invoked by a simple class name + */ + public boolean unsolvedFieldCalledByASimpleClassName(FieldAccessExpr field) { + try { + field.resolve(); + return false; + } catch (UnsolvedSymbolException e) { + // this check is not very comprehensive, since a class can be in lowercase, and a method or + // field can be in uppercase. But since this is without the jar paths, this is the best we can + // do. + return Character.isUpperCase(field.getScope().toString().charAt(0)); + } + } + /** * Returns the fully-qualified class name version of a method call invoked by a simple class name. * @@ -1506,6 +1567,20 @@ public boolean isAnUnsolvedStaticMethodCalledByAQualifiedClassName(MethodCallExp } } + /** + * This method checks if a field access expression is a qualified field signature. For example, + * for this field access expression: org.package.Class.firstField.secondField, this method will + * return true for "org.package.Class.firstField", but not for + * "org.package.Class.firstField.secondField". + * + * @param field the field access expression to be checked + * @return true if field is a qualified field signature + */ + public boolean isAQualifiedFieldSignature(String field) { + String caller = field.substring(0, field.lastIndexOf(".")); + return isAClassPath(caller); + } + /** * Creates a synthetic class corresponding to a static method called by a qualified class name. * Ensure to check with {@link #isAnUnsolvedStaticMethodCalledByAQualifiedClassName} before @@ -1565,6 +1640,62 @@ public void updateClassSetWithQualifiedStaticMethodCall( this.updateMissingClass(classThatContainMethod); } + /** + * Creates a synthetic class corresponding to a static field called by a qualified class name. + * Ensure to check with {@link #isAQualifiedFieldSignature(String)} before calling this method. + * + * @param fieldExpr the field access expression to be used as input. This field access expression + * must be in the form of a qualified class name + * @param isStatic check whether the field is static + */ + public void updateClassSetWithQualifiedFieldSignature(String fieldExpr, boolean isStatic) { + // As this code involves complex string operations, we'll use a field access expression as an + // example, + // following its progression through the code. + // Suppose this is our field access expression: com.example.MyClass.myField + List fieldParts = Splitter.onPattern("[.]").splitToList(fieldExpr); + int numOfFieldParts = fieldParts.size(); + if (numOfFieldParts <= 2) { + throw new RuntimeException( + "Need to check this field access expression with" + + " isAnUnsolvedStaticFieldCalledByAQualifiedClassName before using this method"); + } + String fieldTypeClassName = toCapital(fieldParts.get(0)); + String packageName = fieldParts.get(0); + // According to the above example, fieldName will be myField + String fieldName = fieldParts.get(numOfFieldParts - 1); + @SuppressWarnings( + "signature") // this className is from the second-to-last part of a fully-qualified method + // call, which is the simple name of a class. In this case, it is MyClass. + @ClassGetSimpleName String className = fieldParts.get(numOfFieldParts - 2); + // After this loop: fieldTypeClassName will be ComExample, and packageName will be com.example + for (int i = 1; i < numOfFieldParts - 2; i++) { + fieldTypeClassName = fieldTypeClassName + toCapital(fieldParts.get(i)); + packageName = packageName + "." + fieldParts.get(i); + } + // At this point, fieldTypeClassName will be ComExampleMyClassMyFieldType + fieldTypeClassName = fieldTypeClassName + toCapital(className) + toCapital(fieldName) + "Type"; + // since fieldTypeClassName is just a single long string without any dot in the middle, it will + // be a simple name. + @SuppressWarnings("signature") + @ClassGetSimpleName String thisFieldType = fieldTypeClassName; + UnsolvedClass typeClass = new UnsolvedClass(thisFieldType, packageName); + UnsolvedClass classThatContainField = new UnsolvedClass(className, packageName); + // at this point, fieldDeclaration will become "ComExampleMyClassMyFieldType myField" + String fieldDeclaration = fieldTypeClassName + " " + fieldName; + if (isStatic) { + fieldDeclaration = "static " + fieldDeclaration; + } + // fieldDeclaration will become "static ComExampleMyClassMyFieldType myField = null;" + fieldDeclaration = setInitialValueForVariableDeclaration(fieldTypeClassName, fieldDeclaration); + classThatContainField.addFields(fieldDeclaration); + classAndPackageMap.put(thisFieldType, packageName); + classAndPackageMap.put(className, packageName); + syntheticTypeAndClass.put(thisFieldType, classThatContainField); + this.updateMissingClass(typeClass); + this.updateMissingClass(classThatContainField); + } + /** * Based on the Map returned by JavaTypeCorrect, this method updates the types of methods in * synthetic classes. @@ -1573,6 +1704,14 @@ public void updateClassSetWithQualifiedStaticMethodCall( */ public void updateTypes(Map typeToCorrect) { for (String incorrectType : typeToCorrect.keySet()) { + // update incorrecType if it is the type of a field in a synthetic class + if (syntheticTypeAndClass.containsKey(incorrectType)) { + UnsolvedClass relatedClass = syntheticTypeAndClass.get(incorrectType); + relatedClass.updateFieldByType(incorrectType, typeToCorrect.get(incorrectType)); + this.deleteOldSyntheticClass(relatedClass); + this.createMissingClass(relatedClass); + return; + } // convert MethodNameReturnType to methodName String involvedMethod = incorrectType.substring(0, 1).toLowerCase() diff --git a/src/test/java/org/checkerframework/specimin/UnsolvedStaticQualifiedField.java b/src/test/java/org/checkerframework/specimin/UnsolvedStaticQualifiedField.java new file mode 100644 index 000000000..08e7d963c --- /dev/null +++ b/src/test/java/org/checkerframework/specimin/UnsolvedStaticQualifiedField.java @@ -0,0 +1,18 @@ +package org.checkerframework.specimin; + +import org.junit.Test; + +import java.io.IOException; + +/** + * This test checks that if Specimin will work if there is an unsolved, static field in a qualified name form used by a target method. + */ +public class UnsolvedStaticQualifiedField { + @Test + public void runTest() throws IOException { + SpeciminTestExecutor.runTestWithoutJarPaths( + "unsolvedstaticqualifiedfield", + new String[] {"com/example/Foo.java"}, + new String[] {"com.example.Foo#bar()"}); + } +} diff --git a/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimpleField.java b/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimpleField.java new file mode 100644 index 000000000..648af409c --- /dev/null +++ b/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimpleField.java @@ -0,0 +1,18 @@ +package org.checkerframework.specimin; + +import org.junit.Test; + +import java.io.IOException; + +/** + * This test checks that if Specimin will work if there is an unsolved, static field in a simple name form used by target methods + */ +public class UnsolvedStaticSimpleField { + @Test + public void runTest() throws IOException { + SpeciminTestExecutor.runTestWithoutJarPaths( + "unsolvedstaticsimplefield", + new String[] {"com/example/Foo.java"}, + new String[] {"com.example.Foo#bar()"}); + } +} diff --git a/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimplePrimitiveField.java b/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimplePrimitiveField.java new file mode 100644 index 000000000..b98a211a8 --- /dev/null +++ b/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimplePrimitiveField.java @@ -0,0 +1,18 @@ +package org.checkerframework.specimin; + +import org.junit.Test; + +import java.io.IOException; + +/** + * This test checks that if Specimin will work if there is an unsolved, static field in a simple name form with a primitive type. + */ +public class UnsolvedStaticSimplePrimitiveField { + @Test + public void runTest() throws IOException { + SpeciminTestExecutor.runTestWithoutJarPaths( + "unsolvedstaticsimpleprimitivefield", + new String[] {"com/example/Foo.java"}, + new String[] {"com.example.Foo#bar()"}); + } +} diff --git a/src/test/resources/unsolvednonstaticfield/expected/com/example/Foo.java b/src/test/resources/unsolvednonstaticfield/expected/com/example/Foo.java new file mode 100644 index 000000000..9b1897d84 --- /dev/null +++ b/src/test/resources/unsolvednonstaticfield/expected/com/example/Foo.java @@ -0,0 +1,11 @@ +package com.example; + +import org.sampling.Baz; + +class Foo { + + void test() { + Baz testing = new Baz(); + testing.cal.doAddition(); + } +} diff --git a/src/test/resources/unsolvednonstaticfield/expected/org/sampling/Baz.java b/src/test/resources/unsolvednonstaticfield/expected/org/sampling/Baz.java new file mode 100644 index 000000000..4dae7966a --- /dev/null +++ b/src/test/resources/unsolvednonstaticfield/expected/org/sampling/Baz.java @@ -0,0 +1,10 @@ +package org.sampling; + +public class Baz { + + public OrgSamplingBazCalType cal; + + public Baz() { + throw new Error(); + } +} diff --git a/src/test/resources/unsolvednonstaticfield/expected/org/sampling/DoAdditionReturnType.java b/src/test/resources/unsolvednonstaticfield/expected/org/sampling/DoAdditionReturnType.java new file mode 100644 index 000000000..77298e3c2 --- /dev/null +++ b/src/test/resources/unsolvednonstaticfield/expected/org/sampling/DoAdditionReturnType.java @@ -0,0 +1,4 @@ +package org.sampling; + +public class DoAdditionReturnType { +} diff --git a/src/test/resources/unsolvednonstaticfield/expected/org/sampling/OrgSamplingBazCalType.java b/src/test/resources/unsolvednonstaticfield/expected/org/sampling/OrgSamplingBazCalType.java new file mode 100644 index 000000000..5f188b6a5 --- /dev/null +++ b/src/test/resources/unsolvednonstaticfield/expected/org/sampling/OrgSamplingBazCalType.java @@ -0,0 +1,8 @@ +package org.sampling; + +public class OrgSamplingBazCalType { + + public DoAdditionReturnType doAddition() { + throw new Error(); + } +} diff --git a/src/test/resources/unsolvednonstaticfield/input/com/example/Foo.java b/src/test/resources/unsolvednonstaticfield/input/com/example/Foo.java new file mode 100644 index 000000000..18633fc64 --- /dev/null +++ b/src/test/resources/unsolvednonstaticfield/input/com/example/Foo.java @@ -0,0 +1,8 @@ +package com.example; +import org.sampling.Baz; +class Foo { + void test() { + Baz testing = new Baz(); + testing.cal.doAddition(); + } +} \ No newline at end of file diff --git a/src/test/resources/unsolvedstaticqualifiedfield/expected/com/example/Foo.java b/src/test/resources/unsolvedstaticqualifiedfield/expected/com/example/Foo.java new file mode 100644 index 000000000..86f58eebf --- /dev/null +++ b/src/test/resources/unsolvedstaticqualifiedfield/expected/com/example/Foo.java @@ -0,0 +1,8 @@ +package com.example; + +class Foo { + + void bar() { + org.sampling.Baz.myField.doAddition(); + } +} diff --git a/src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/Baz.java b/src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/Baz.java new file mode 100644 index 000000000..75915890a --- /dev/null +++ b/src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/Baz.java @@ -0,0 +1,6 @@ +package org.sampling; + +public class Baz { + + public static OrgSamplingBazMyFieldType myField; +} diff --git a/src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/DoAdditionReturnType.java b/src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/DoAdditionReturnType.java new file mode 100644 index 000000000..77298e3c2 --- /dev/null +++ b/src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/DoAdditionReturnType.java @@ -0,0 +1,4 @@ +package org.sampling; + +public class DoAdditionReturnType { +} diff --git a/src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/OrgSamplingBazMyFieldType.java b/src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/OrgSamplingBazMyFieldType.java new file mode 100644 index 000000000..4ad6473a6 --- /dev/null +++ b/src/test/resources/unsolvedstaticqualifiedfield/expected/org/sampling/OrgSamplingBazMyFieldType.java @@ -0,0 +1,8 @@ +package org.sampling; + +public class OrgSamplingBazMyFieldType { + + public DoAdditionReturnType doAddition() { + throw new Error(); + } +} diff --git a/src/test/resources/unsolvedstaticqualifiedfield/input/com/example/Foo.java b/src/test/resources/unsolvedstaticqualifiedfield/input/com/example/Foo.java new file mode 100644 index 000000000..b69710755 --- /dev/null +++ b/src/test/resources/unsolvedstaticqualifiedfield/input/com/example/Foo.java @@ -0,0 +1,8 @@ +package com.example; + +class Foo { + void bar() { + org.sampling.Baz.myField.doAddition(); + } +} + diff --git a/src/test/resources/unsolvedstaticsimplefield/expected/com/example/Foo.java b/src/test/resources/unsolvedstaticsimplefield/expected/com/example/Foo.java new file mode 100644 index 000000000..35d649568 --- /dev/null +++ b/src/test/resources/unsolvedstaticsimplefield/expected/com/example/Foo.java @@ -0,0 +1,10 @@ +package com.example; + +import org.sampling.Baz; + +class Foo { + + void bar() { + Baz.myField.doAddition(); + } +} diff --git a/src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/Baz.java b/src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/Baz.java new file mode 100644 index 000000000..75915890a --- /dev/null +++ b/src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/Baz.java @@ -0,0 +1,6 @@ +package org.sampling; + +public class Baz { + + public static OrgSamplingBazMyFieldType myField; +} diff --git a/src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/DoAdditionReturnType.java b/src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/DoAdditionReturnType.java new file mode 100644 index 000000000..77298e3c2 --- /dev/null +++ b/src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/DoAdditionReturnType.java @@ -0,0 +1,4 @@ +package org.sampling; + +public class DoAdditionReturnType { +} diff --git a/src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/OrgSamplingBazMyFieldType.java b/src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/OrgSamplingBazMyFieldType.java new file mode 100644 index 000000000..4ad6473a6 --- /dev/null +++ b/src/test/resources/unsolvedstaticsimplefield/expected/org/sampling/OrgSamplingBazMyFieldType.java @@ -0,0 +1,8 @@ +package org.sampling; + +public class OrgSamplingBazMyFieldType { + + public DoAdditionReturnType doAddition() { + throw new Error(); + } +} diff --git a/src/test/resources/unsolvedstaticsimplefield/input/com/example/Foo.java b/src/test/resources/unsolvedstaticsimplefield/input/com/example/Foo.java new file mode 100644 index 000000000..7a7b45660 --- /dev/null +++ b/src/test/resources/unsolvedstaticsimplefield/input/com/example/Foo.java @@ -0,0 +1,8 @@ +package com.example; +import org.sampling.Baz; + +class Foo { + void bar() { + Baz.myField.doAddition(); + } +} diff --git a/src/test/resources/unsolvedstaticsimpleprimitivefield/expected/com/example/Foo.java b/src/test/resources/unsolvedstaticsimpleprimitivefield/expected/com/example/Foo.java new file mode 100644 index 000000000..65e2b04ae --- /dev/null +++ b/src/test/resources/unsolvedstaticsimpleprimitivefield/expected/com/example/Foo.java @@ -0,0 +1,10 @@ +package com.example; + +import org.sampling.Baz; + +class Foo { + + void bar() { + int z = Baz.myField; + } +} diff --git a/src/test/resources/unsolvedstaticsimpleprimitivefield/expected/org/sampling/Baz.java b/src/test/resources/unsolvedstaticsimpleprimitivefield/expected/org/sampling/Baz.java new file mode 100644 index 000000000..181cd7944 --- /dev/null +++ b/src/test/resources/unsolvedstaticsimpleprimitivefield/expected/org/sampling/Baz.java @@ -0,0 +1,6 @@ +package org.sampling; + +public class Baz { + + public static int myField; +} diff --git a/src/test/resources/unsolvedstaticsimpleprimitivefield/input/com/example/Foo.java b/src/test/resources/unsolvedstaticsimpleprimitivefield/input/com/example/Foo.java new file mode 100644 index 000000000..f362b64b2 --- /dev/null +++ b/src/test/resources/unsolvedstaticsimpleprimitivefield/input/com/example/Foo.java @@ -0,0 +1,8 @@ +package com.example; +import org.sampling.Baz; + +class Foo { + void bar() { + int z = Baz.myField; + } +} From 9eaf3e2d4ee2f59d872642d13c3f636be9ac552b Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 1 Dec 2023 20:50:50 -0500 Subject: [PATCH 2/5] clarify docs and apply spotless --- .../checkerframework/specimin/UnsolvedSymbolVisitor.java | 6 +++--- .../specimin/UnsolvedStaticQualifiedField.java | 6 +++--- .../specimin/UnsolvedStaticSimpleField.java | 6 +++--- .../specimin/UnsolvedStaticSimplePrimitiveField.java | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java b/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java index b6ec341ef..1a1844c26 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java @@ -1665,8 +1665,8 @@ public void updateClassSetWithQualifiedFieldSignature(String fieldExpr, boolean // According to the above example, fieldName will be myField String fieldName = fieldParts.get(numOfFieldParts - 1); @SuppressWarnings( - "signature") // this className is from the second-to-last part of a fully-qualified method - // call, which is the simple name of a class. In this case, it is MyClass. + "signature") // this className is from the second-to-last part of a fully-qualified field + // signature, which is the simple name of a class. In this case, it is MyClass. @ClassGetSimpleName String className = fieldParts.get(numOfFieldParts - 2); // After this loop: fieldTypeClassName will be ComExample, and packageName will be com.example for (int i = 1; i < numOfFieldParts - 2; i++) { @@ -1684,9 +1684,9 @@ public void updateClassSetWithQualifiedFieldSignature(String fieldExpr, boolean // at this point, fieldDeclaration will become "ComExampleMyClassMyFieldType myField" String fieldDeclaration = fieldTypeClassName + " " + fieldName; if (isStatic) { + // fieldDeclaration will become "static ComExampleMyClassMyFieldType myField = null;" fieldDeclaration = "static " + fieldDeclaration; } - // fieldDeclaration will become "static ComExampleMyClassMyFieldType myField = null;" fieldDeclaration = setInitialValueForVariableDeclaration(fieldTypeClassName, fieldDeclaration); classThatContainField.addFields(fieldDeclaration); classAndPackageMap.put(thisFieldType, packageName); diff --git a/src/test/java/org/checkerframework/specimin/UnsolvedStaticQualifiedField.java b/src/test/java/org/checkerframework/specimin/UnsolvedStaticQualifiedField.java index 08e7d963c..3d8dda22b 100644 --- a/src/test/java/org/checkerframework/specimin/UnsolvedStaticQualifiedField.java +++ b/src/test/java/org/checkerframework/specimin/UnsolvedStaticQualifiedField.java @@ -1,11 +1,11 @@ package org.checkerframework.specimin; -import org.junit.Test; - import java.io.IOException; +import org.junit.Test; /** - * This test checks that if Specimin will work if there is an unsolved, static field in a qualified name form used by a target method. + * This test checks that if Specimin will work if there is an unsolved, static field in a qualified + * name form used by a target method. */ public class UnsolvedStaticQualifiedField { @Test diff --git a/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimpleField.java b/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimpleField.java index 648af409c..fb259bc39 100644 --- a/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimpleField.java +++ b/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimpleField.java @@ -1,11 +1,11 @@ package org.checkerframework.specimin; -import org.junit.Test; - import java.io.IOException; +import org.junit.Test; /** - * This test checks that if Specimin will work if there is an unsolved, static field in a simple name form used by target methods + * This test checks that if Specimin will work if there is an unsolved, static field in a simple + * name form used by target methods */ public class UnsolvedStaticSimpleField { @Test diff --git a/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimplePrimitiveField.java b/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimplePrimitiveField.java index b98a211a8..ff5bb33b8 100644 --- a/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimplePrimitiveField.java +++ b/src/test/java/org/checkerframework/specimin/UnsolvedStaticSimplePrimitiveField.java @@ -1,11 +1,11 @@ package org.checkerframework.specimin; -import org.junit.Test; - import java.io.IOException; +import org.junit.Test; /** - * This test checks that if Specimin will work if there is an unsolved, static field in a simple name form with a primitive type. + * This test checks that if Specimin will work if there is an unsolved, static field in a simple + * name form with a primitive type. */ public class UnsolvedStaticSimplePrimitiveField { @Test From 8a1f84b0d51fcc9eff5fd6299636cf2c6faf1752 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 2 Dec 2023 12:57:17 -0500 Subject: [PATCH 3/5] add test file --- .../specimin/UnsolvedNonStaticField.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/test/java/org/checkerframework/specimin/UnsolvedNonStaticField.java diff --git a/src/test/java/org/checkerframework/specimin/UnsolvedNonStaticField.java b/src/test/java/org/checkerframework/specimin/UnsolvedNonStaticField.java new file mode 100644 index 000000000..cbc57db92 --- /dev/null +++ b/src/test/java/org/checkerframework/specimin/UnsolvedNonStaticField.java @@ -0,0 +1,18 @@ +package org.checkerframework.specimin; + +import org.junit.Test; + +import java.io.IOException; + +/** + * This test checks that if Specimin will work if there is an unsolved, non-static field used by target methods + */ +public class UnsolvedNonStaticField { + @Test + public void runTest() throws IOException { + SpeciminTestExecutor.runTestWithoutJarPaths( + "unsolvednonstaticfield", + new String[] {"com/example/Foo.java"}, + new String[] {"com.example.Foo#test()"}); + } +} From 97f9232e29bc7cc8e8cbbf75de3124602006ab08 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 2 Dec 2023 13:00:21 -0500 Subject: [PATCH 4/5] change isStatic to String --- .../org/checkerframework/specimin/UnsolvedClass.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedClass.java b/src/main/java/org/checkerframework/specimin/UnsolvedClass.java index f83342649..1f8922422 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedClass.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedClass.java @@ -165,10 +165,10 @@ public void updateFieldByType(String currentType, String correctType) { Set newFields = new HashSet<>(); while (iterator.hasNext()) { String fieldDeclared = iterator.next(); - boolean isStatic = false; + String staticKeyword = ""; if (fieldDeclared.startsWith("static")) { fieldDeclared = fieldDeclared.replace("static ", ""); - isStatic = true; + staticKeyword = "static "; } List elements = Splitter.on(' ').splitToList(fieldDeclared); // fieldExpression is guaranteed to have the form "TYPE FIELD_NAME". Since this field @@ -178,15 +178,9 @@ public void updateFieldByType(String currentType, String correctType) { String fieldName = elements.get(1); if (fieldType.equals(currentType)) { iterator.remove(); - if (!isStatic) { newFields.add( UnsolvedSymbolVisitor.setInitialValueForVariableDeclaration( - correctType, correctType + " " + fieldName)); - } else { - newFields.add( - UnsolvedSymbolVisitor.setInitialValueForVariableDeclaration( - correctType, "static " + correctType + " " + fieldName)); - } + correctType, staticKeyword + correctType + " " + fieldName)); } } From 4a181cb3e3842b25bb0503c37fe53c37ff0e4f22 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 2 Dec 2023 13:13:59 -0500 Subject: [PATCH 5/5] apply spotless and clarify comments --- .../java/org/checkerframework/specimin/UnsolvedClass.java | 6 +++--- .../checkerframework/specimin/UnsolvedSymbolVisitor.java | 1 + .../checkerframework/specimin/UnsolvedNonStaticField.java | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedClass.java b/src/main/java/org/checkerframework/specimin/UnsolvedClass.java index 1f8922422..5d12e4ecf 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedClass.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedClass.java @@ -178,9 +178,9 @@ public void updateFieldByType(String currentType, String correctType) { String fieldName = elements.get(1); if (fieldType.equals(currentType)) { iterator.remove(); - newFields.add( - UnsolvedSymbolVisitor.setInitialValueForVariableDeclaration( - correctType, staticKeyword + correctType + " " + fieldName)); + newFields.add( + UnsolvedSymbolVisitor.setInitialValueForVariableDeclaration( + correctType, staticKeyword + correctType + " " + fieldName)); } } diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java b/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java index 1a1844c26..5664e2a13 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java @@ -1660,6 +1660,7 @@ public void updateClassSetWithQualifiedFieldSignature(String fieldExpr, boolean "Need to check this field access expression with" + " isAnUnsolvedStaticFieldCalledByAQualifiedClassName before using this method"); } + // this is the synthetic type of the field String fieldTypeClassName = toCapital(fieldParts.get(0)); String packageName = fieldParts.get(0); // According to the above example, fieldName will be myField diff --git a/src/test/java/org/checkerframework/specimin/UnsolvedNonStaticField.java b/src/test/java/org/checkerframework/specimin/UnsolvedNonStaticField.java index cbc57db92..904590b2a 100644 --- a/src/test/java/org/checkerframework/specimin/UnsolvedNonStaticField.java +++ b/src/test/java/org/checkerframework/specimin/UnsolvedNonStaticField.java @@ -1,11 +1,11 @@ package org.checkerframework.specimin; -import org.junit.Test; - import java.io.IOException; +import org.junit.Test; /** - * This test checks that if Specimin will work if there is an unsolved, non-static field used by target methods + * This test checks that if Specimin will work if there is an unsolved, non-static field used by + * target methods */ public class UnsolvedNonStaticField { @Test